RSA 加密 / 解密

在瀏覽器中生成 RSA 金鑰對,用 RSA-OAEP 加密解密,用 RSA-PSS 或 PKCS#1 v1.5 簽名,並在 SPKI、PKCS#8 與 PKCS#1 PEM 格式之間轉換金鑰。一切都在本地用 WebCrypto 引擎完成 —— 金鑰和訊息絕不離開本頁。
RSA 的用途:小訊息和簽名,不是批次資料。RSA 單次最多加密 k−2·hashLen−2 位元組(2048 位金鑰配 SHA-256 時為 190 位元組),而且比 AES 慢約 1000 倍。真實系統用的是混合加密:RSA 包裹一把隨機 AES 金鑰,AES 再加密資料(參見 AES 工具)。本頁用於學習、測試和互操作驗證。
模長:
雜湊: 標籤:
PKCS#1 v1.5 加密是刻意不實現的:它沒有密文完整性,會敗給填充預言攻擊(Bleichenbacher)。OAEP 是新設計唯一應該使用的加密模式。
方案: 雜湊:
方案: 雜湊:
一次 RSA 呼叫能裝多少? OAEP 訊息上限 = 金鑰位元組數 − 2×雜湊位元組數 − 2:
模長SHA-256SHA-384SHA-512
1024 位62 位元組30 位元組0 位元組
2048 位190 位元組126 位元組62 位元組
3072 位318 位元組254 位元組190 位元組
4096 位446 位元組382 位元組318 位元組
簽名沒有訊息長度限制 —— 訊息先做雜湊;簽名本身總是恰好等於金鑰長度(2048 位金鑰為 256 位元組)。
金鑰強度與生命週期 (NIST SP 800-57 第 1 部分第 5 版):1024 位自 2013 年起不允許用於新用途;2048 位是當前的最低標準,可用至 2030 年;3072 位及以上是 2031 年以後的從容之選。8192 位在本頁可用,但瀏覽器中生成可能耗時一分鐘以上,而且買到的安全餘量目前誰也用不上 —— 建議 3072/4096。
與 OpenSSL 互操作 —— 本頁生成的 PEM 可在命令列往返使用:
openssl pkeyutl -encrypt -pubin -inkey pub.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256
openssl pkeyutl -decrypt -inkey priv.pem -pkeyopt rsa_padding_mode:oaep ...
openssl dgst -sha256 -sign priv.pem -out msg.sig msg.txt(PKCS#1 v1.5)/ openssl dgst -sha256 -verify pub.pem -signature msg.sig msg.txt
PSS:在兩條 dgst 命令中加 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32。金鑰格式轉換:openssl rsa -in key.pem -RSAPublicKey -out pubkey.pem 可在磁碟上做 SPKI ↔ PKCS#1 轉換;本頁則在瀏覽器裡完成。
自檢
用獨立的錨點資料重跑本頁引擎:三種模長的金鑰生成、OAEP 加解密往返、PSS 與 PKCS#1 v1.5 在各雜湊下的簽名驗籤、一條逐位元組確定的 v1.5 簽名向量(由獨立的原生實現生成)、一條由 OpenSSL 生成的固定 OAEP 密文與 PSS 簽名(本頁必須能解密/驗證,跨實現互操作)、PEM 格式轉換往返,以及反向測試(被篡改的簽名、錯誤的金鑰必須失敗)。不會向任何地方傳送資料。
從零講清 RSA 加密與簽名
一句話:RSA 是公鑰密碼 —— 任何人都能用公鑰給你加密或驗證你的簽名,但只有私鑰能解密或簽名 —— 它的安全性建立在“把一個 2048 位的數分解成兩個素數是現有任何計算機都做不到的”這一事實之上。
RSA 到底在算什麼

兩個素數與一扇活板門。RSA 金鑰始於兩個大隨機素數 p 和 q;它們的乘積 n(模數,即“2048 位金鑰”裡的 2048 位)連同指數 e(幾乎總是 65537)一起公佈在公鑰裡。私鑰是 d —— e 的模逆元,由 p、q 算出 —— 外加由素數匯出的幾條捷徑,能把私鑰運算提速 4 倍。

加密和簽名是同一種冪運算。加密訊息塊 m 就是算 me mod n;解密是 cd mod n。用私鑰指數對預格式化的塊做運算、再用公鑰驗證,本質上是同一運算的另一個方向。本頁其餘的一切 —— OAEP、PSS、PKCS#1 —— 都是在安全地格式化那個塊,使裸冪運算從不直接暴露。

為什麼它慢。2048 位金鑰上的一次 RSA 運算,CPU 開銷約是 AES 加密一個分組的一百萬倍。這就是 TLS 只用 RSA(或 ECC)協商一把隨機 AES 金鑰、然後切換到對稱加密的原因。你自己構建系統時同理:RSA 管金鑰,AES 管資料。

金鑰長度。模長就是安全旋鈕:1024 位屬於“政策性告破”(分解已接近國家級能力),2048 位 ≈ 112 位對稱強度,3072 位 ≈ 128 位,15360 位才相當於 256 位對稱金鑰。後量子備註:Shor 演算法在足夠大的量子計算機上會直接擊潰 RSA —— 這也是要把金鑰生命週期限定在有限年限內的又一個理由。

OAEP:唯一應該使用的加密填充

裸 RSA 的問題。教科書 RSA(對原始訊息直接算 me mod n)是確定且可塑的:相同訊息加密結果相同,攻擊者可以把密文乘上“2 的加密”使明文翻倍,短訊息還會敗給中間相遇。它絕不能直接使用。

OAEP 做了什麼。OAEP(PKCS#1 v2,RFC 8017)在冪運算之前,通過一個基於雜湊的 Feistel 網路把訊息與隨機種子混合。效果:加密隨機化(同一明文每次密文都不同 —— 把 OAEP 示例跑兩遍對比即可見)、解密時可檢出篡改,而且訊息必須連同雜湊的餘量一起裝進模數 —— 即上面表格裡的 k−2·hashLen−2 上限。

標籤。一個可選的、繫結進填充的公開字串。兩側必須提供完全相同的標籤,否則解密失敗 —— 就像 AES-GCM 裡的 AAD,它讓你把密文繫結到上下文(檔名、演算法編號等)。留空是常態。

PKCS#1 v1.5 加密是刻意缺席的。1993 年代的這種填充沒有完整性,還會通過解密報錯行為洩露資訊(Bleichenbacher 1998 年的攻擊用幾百萬個特製密文把填充預言變成完整解密;它至今仍能攻破真實系統,即 2017 年的 ROBOT)。WebCrypto 拒絕實現它,本頁也一樣。

PSS 與 PKCS#1 v1.5 簽名

簽名 = 雜湊,再填充,再冪運算。兩種方案都先對訊息雜湊(此處為 SHA-256/384/512),把摘要格式化成塊,再施加私鑰指數。驗證用公鑰重新格式化塊並比對。區別在格式化方式:

方案塊內容性質
RSASSA-PKCS1-v1_5摘要 + digest-info 頭,確定性同一訊息的每次簽名逐位元組相同(見自檢:其 v1.5 向量是固定的)。舊系統普遍支援:X.509 證書、JWT RS*、TLS 1.3 之前。在驗證實現正確的前提下沒有已知偽造。
RSA-PSS摘要 + 雜湊長度的隨機鹽,MGF1 掩碼隨機化 —— 每次簽名都不同。在隨機預言模型下可證安全;TLS 1.3 的未來證書和更新協議要求使用。驗證方必須知道鹽長(本頁用雜湊長度,即常見預設值)。

怎麼選。新設計:PSS。要與現有證書、JWT(RS256)或舊系統互通:v1.5。兩者作為簽名方案都是安全的 —— v1.5 的黑歷史說的是*加密*填充,那是另一種構造,只是名字像。

PKCS#1、PKCS#8 與 SPKI:同一把金鑰為什麼有四種 PEM

PEM 只是套了殼的 base64 DER。PEM 檔案 = 一個標籤 + 一段 DER(二進位制 ASN.1)結構的 base64。標籤告訴你裡面是哪種結構 —— 這就是本頁生成的四種格式的全部區別:

PEM 標籤DER 結構內容
-----BEGIN PUBLIC KEY-----SubjectPublicKeyInfo(SPKI)演算法 OID + 公鑰(n, e)。各處預設的“公鑰”。
-----BEGIN RSA PUBLIC KEY-----PKCS#1 RSAPublicKey裸的(n, e)。老版 OpenSSL 和一些嵌入式庫。
-----BEGIN PRIVATE KEY-----PKCS#8 PrivateKeyInfo演算法 OID + 私鑰。演算法無關的容器,現代預設。
-----BEGIN RSA PRIVATE KEY-----PKCS#1 RSAPrivateKeyn, e, d, p, q, …。經典 OpenSSL genrsa 的輸出。

同一把金鑰,不同的包裝。點“生成”後上面四個框裡是同一金鑰對 —— SPKI 與 PKCS#1 公鑰含完全相同的 n、e;PKCS#8 與 PKCS#1 私鑰含完全相同的私鑰值。本頁在瀏覽器裡無損地互相轉換(自檢會往返驗證這些轉換)。在工具之間貼上金鑰時報“金鑰無效”,最常見的原因就是搞混了格式。

加密的 PEM(-----BEGIN ENCRYPTED PRIVATE KEY-----)是帶口令加密層的 PKCS#8。本頁不接受它們 —— 請先離線解密(openssl pkcs8 -in enc.pem -out plain.pem)。

常見錯誤
  • 用 RSA 加密批次資料。除了 OAEP 的硬性長度上限,它還慢得離譜。正確的模式是:生成隨機 AES 金鑰,用 RSA-OAEP 加密這把 AES 金鑰,用 AES-GCM 加密資料,兩部分一起傳送。
  • 因為“只是測試”就把私鑰發出去。本頁向量裡內嵌的示例金鑰是刻意公開的 —— 它們只能籤測試資料。你的金鑰不是這樣。
  • 用私鑰做驗證。數學上通常“行得通”,有些工具也允許,但把私鑰發給驗證方就毀了整個模型。驗證請用公鑰(本頁強制如此)。
  • 逐位元組比較簽名來判斷程式碼對不對。PSS(和 OAEP)是隨機化的:兩次正確的執行產生不同輸出。要比較就用驗證來比,不要比位元組相等 —— 只有 v1.5 簽名是確定性的。
  • “因為快”就用 1024 位金鑰。它同時也進入了資金充裕者的分解射程。速度論據已隨混合加密而死:RSA 每個會話只跑一次,不是每個位元組跑一次。
  • 給 PSS 硬編碼一個不等於雜湊長度的鹽長。驗證方必須一致;雜湊長度(本頁的選擇,也是 OpenSSL 的 -sigopt rsa_pss_saltlen:32)才是可互通的預設值。
常見問題

為什麼我的解密失敗,提示“金鑰錯、雜湊錯或密文損壞”?OAEP 解密在以下任一情況都會失敗:不是配對的私鑰、雜湊與加密方不同、標籤不同,或密文任一位元組被改過。錯誤提示刻意對所有情況完全一致 —— 洩露“哪項檢查失敗了”本身就是一次填充預言洩露。

能從私鑰匯出公鑰嗎?能 —— 私鑰格式裡同樣含 n 和 e。本頁在“生成”後把兩者都展示出來;OpenSSL 用 openssl pkey -in priv.pem -pubout 實現。

為什麼指數是 65537?它是素數,二進位制只有兩個 1(冪運算快),又足夠大,能避開 e=3 時代的低指數攻擊。1990 年代起它就是標準選擇;“奇怪”的指數通常意味著弱金鑰或非標準金鑰。

RSA 因為量子計算機死了?還沒有 —— 目前沒有任何機器能在 2048 位模數上跑 Shor 演算法。但必須保密到 2030 年代以後的資料已經在遷移;PQC 混合方案是工程上的答案。

那 openssl rsautl 呢?它是 pkeyutl 的廢棄前身。預設用 v1.5 加密填充 —— 這也是它被淘汰的又一個原因。