雜記 › 技術筆記 › 13 格演算法表與 7 道驗簽閘門:JWT 工具頁驗簽側的分支比解碼側更窄,逐演算法真簽真驗矩陣

13 格演算法表與 7 道驗簽閘門:JWT 工具頁驗簽側的分支比解碼側更窄,逐演算法真簽真驗矩陣

同一枚 JWT 貼進解碼頁,讀得出內容卻驗不過簽名:ALGS 的 13 格只寫了三類欄位、12 演算法乘以 4 個入口的真簽真驗矩陣、簽名段位元組跟金鑰跟雜湊不跟演算法名、七道閘門裡六道走在讀金鑰之前,逐筆量過。

同一枚 JWT 讀得出內容,卻驗不過簽名

一枚 RS256 權杖貼進解碼頁,標頭與載荷照樣解碼,點「驗證簽名」卻回「還沒有輸入金鑰」。把範本整段 script 丟進 node:vm 跑一遍才明白:這一頁把「讀」和「驗」做成兩套判定,驗簽側窄到只有 13 個演算法名進得了門,前六道閘門還不看你填的金鑰。

被測的是 JWT 解析 那一頁,768 行、38463 位元組,帳目在 ALGS(L199–213)、pemToDer(L604)、doVerify(L648)三處;權杖與金鑰全用 demo- 前綴自簽。

13 格裡只寫了三類欄位

Object.keys 數出 13 個鍵,即圖 1 左欄。hashByte 只在 HS 三檔(32/48/64),salt 只在 PS 三檔(32/48/64),curve 只在 ES 三檔(P-256、P-384、P-521),餘下 10 格三類欄位全無,none 只有 family。

最容易上當的是 ES512:鍵裡 512、曲線卻是 P-521,差 9;座標每段 66 位元組,簽名 132 是 2×66 而非 2×64。

13 個演算法鍵 → family → 面板開關 HS ×3 · hashByte 32/48/64 hmac HMAC 框 RS ×3 · 三類欄位全無 rsa 公鑰框 PS ×3 · salt 32/48/64 pss 公鑰框 EC ×3 · curve P-521 ec 公鑰框 none · 只有 family 一格 none 三元素全隱藏

圖 1:欄位決定家族,家族決定面板顯隱。

簽名段位元組跟金鑰跟雜湊,不跟演算法名

  • HS 三檔 32、48、64 位元組,跟 hash,與那三格 hashByte 一一對上。
  • RS256@2048 與 RS384@2048 同為 256,RS512@4096 才 512,PS 三檔同帳。
  • ES256 是 64、ES384 是 96、ES512 是 132,跟曲線。

換成 base64url 字元數就是圖 2 末行十二檔,與 ceil(bytes/3)*4 減補位在 12 行逐行相等。

位元組數(柱高)與字元數(末行) 32 48 64 64 96 132 256 256 512 HS256 HS384 HS512 ES256 ES384 ES512 RS256 RS384 RS512 43 64 86 86 128 176 342 342 683

圖 2:兩根同高柱子來自 2048 位的 RS256 與 RS384。

12 演算法 × 4 入口,只有兩種結果

每行權杖的 kid 寫成 demo-key-1、載荷 lvl 寫成 3,HS 金鑰 demo-shared-key-2026-b22,非對稱現生成 RSA-2048/4096 與三條 P 曲線的 PEM。四個入口只有兩種結果:正確金鑰與匯出成 JWK 各 12/12 通過;lvl 從 3 改成 4 而簽名段原樣、或換一把同參數錯鑰,都是 12/12 不匹配。

金鑰進門先看首字元

importKey 先 trim,再看首字元是不是 {:能解析走 jwk,否則 hmac 落 raw、其餘落 spki。同一段金鑰寫成 raw 與寫成 JWK,重算簽名逐位元組一致,32 位元組、64 字元 hex。坑是 {"a":1}:解析得動、進 jwk 支拋 DataError,message 無中文,回落成「貼上的金鑰內容不完整,或與標頭聲明的演算法不匹配」。

PEM 的三條拒絕加一次救回

BEGIN/END 不匹配報「沒有識別到 PEM 的 BEGIN/END 標記」,裸 base64 也落這條;label 不是 PUBLIC KEY 就回顯識別到的名字(CERTIFICATE、RSA PUBLIC KEY 各一條);BEGIN 與 END 不對稱時反向引用失配,仍落第一條。body 先去空白、再把 - 換 +、_ 換 / 才進 atob,失敗報「Base64 無法還原(長度 N)」,N 是去空白後的字元數。一次救回:RSA-2048 demo 公鑰的 SPKI DER 294 位元組編成 392 字元,19 枚 + 與 / 全換成 base64url 寫法後兩次輸出逐位元組一致;另一把 15 枚。字元表差別見 Base64。

七道閘門,六道走在讀金鑰之前

閘門觸發回顯
L650沒有權杖只 toast「先貼上一個權杖」
L6515 段 JWE「無法驗簽」
L654alg 為 none「沒有簽名可驗」
L658家族不在四類「不在本機可驗證的範圍內」
L663沒有 WebCrypto瀏覽器口徑,未實測
L667缺第 3 段「缺少簽名段」
L675金鑰框空白「還沒有輸入金鑰」

七道都在金鑰框留空下跑過,除最後一道外無一回顯金鑰文案,前六道在 L672 讀 keyText 前返回。第四道餵了 6 個輸入:HS224、ES256K、EdDSA、hs256、A128GCM 與標頭不帶 alg,全落這條,查表大小寫敏感。

兩條分支失敗時回吐的東西不一樣

HMAC 是自己重算再比:sign 得到 expect 交給 equalBytes(先比 length,再定長 XOR-OR 摺疊),失敗時兩段各印前 8 位元組,恆為 16 個十六進位字元加一枚省略號。RS、PS、EC 走 verify,只回布林值,「簽名 N 位元組」只在成功文案裡;255 位元組的 RS256 與 63 位元組的 ES256 截短簽名在 Node 側返回 false,進「簽名不匹配」而不報錯。

salt 也不反映在長度上:PS256 的 saltLength 被寫死成 32,同一把 RSA-PSS-2048 下 salt=20 簽發的簽名仍是 256 位元組 342 字元,卻驗不過,面板也不提 salt。

口徑補一句:除表中標註外全部為純 Node 實測;DOM 像素、渲染計時器觸發次數、非對稱庫對截短簽名是否拋錯,本批未開瀏覽器,記未實測。

三個面板開關各自聽 family 的

jwt-hs-box 只在 hmac 開,jwt-pk-box 只在 rsa/pss/ec 開,按鈕取兩者的或。八種輸入:沒有 alg、未知串 XYZALG、none 三種三元素全隱藏;HS256 只開 HMAC 框;RS256、PS384、ES512 只開公鑰框;塞一段長度除以 4 餘 1 的壞簽名,開關不動,只有那一行中段從「N 位元組」換成「無法解碼」。

常見問題

Q:同一個 JWT 貼進去,為什麼這一頁讀得出內容卻驗不過簽名?

兩套判定:解碼只要求段能切開、位元組能還原,驗簽要先過閘門——alg 得在 13 格裡、家族在四類內、真有第 3 段。三段結構與 exp 的讀法見站內那篇 舊文。

Q:簽名段是 256 位元組,就一定是 RS256 嗎?

不一定,RS 與 PS 在 2048 位下都是 256 位元組 342 字元;跟雜湊的只有 HS 三檔,跟曲線的只有 ES 三檔。

Q:金鑰填了,為什麼還說沒有輸入金鑰?

要麼填錯了框——顯隱由 alg 決定,落在四類外或沒有 alg 時連按鈕一起隱藏;要麼那一格只有一串空格。

📎 相關文章

8 個元素算得出 40320 條卻只列 5000 行:排列計算器的兩道互不相干的上限、重複元素按 3! 計數與 n≤20 的過程斷點 2026-11-12 投放連結的裝配順序與大小寫摺疊:UTM 產生器六態標註、兩道硬上限與五欄各用一把尺實測 2026-11-12 拖到 4 和拖到 8,螢幕上都是 43890 個塊:馬賽克預覽與導出的兩套塊網格、三檔會崩與 6px 誤觸閾值 2026-11-10 位圖進 SVG 只是換了個盒子:232 字節的容器常數、一個從不生效的「base64 嵌入」勾選,與四檔縮放下紋絲不動的 142064 個字元 2026-11-10 轉完悄悄少了 271 個字符:Markdown 轉 HTML 的洞協議、64 輪回填與 20 條提示上限 2026-11-10