491 字
2 分鐘
[範例] node-forge 公鑰驗證 PKCS1 簽章值 | 自然人憑證開發筆記

前文說明過從憑證提取公鑰進行驗簽,也可以直接透過公鑰驗簽,在 /pkcs11info?withcert=true 可以取得公鑰。

Demo
前端程式碼
驗證 PKCS1 時,我們需要 TBS、Hash 演算法、公鑰、簽章值,Javascript 用來讀取簽章公鑰。
<h1>自然人憑證 公鑰驗簽PKCS1</h1><input type="button" value="讀取簽章公鑰" onclick="getCert()" /><form action="https://cert.modatw.workers.dev/api/pkcs1VerifyWithKey" method="post"> tbs: <input type="text" name="tbs" /><br> hashAlgorithm: <select name="hashAlgorithm"> <option value="SHA1">SHA1</option> <option value="SHA256" selected>SHA256</option> <option value="SHA384">SHA384</option> <option value="SHA512">SHA512</option> </select><br> signature (簽章值)<br> <textarea name="signature" rows="8" cols="65"></textarea><br> keyb64 (公鑰)<br> <textarea name="keyb64" rows="8" cols="65"></textarea><br> <input type="submit"></form>
<script> let popupForm; let timeout;
function getCert() { popupForm = window.open( "http://localhost:61161/popupForm", "popupForm", "height=200, width=200, left=100, top=20" ); timeout = setTimeout(() => { popupForm.close(); alert("尚未安裝元件"); }, 5000); }
function receiveMessage(event) { if (event.origin !== "http://localhost:61161") return;
let ret = JSON.parse(event.data); if (ret.func === "getTbs") { clearTimeout(timeout); const tbsData = { func: "GetUserCert" }; popupForm.postMessage(JSON.stringify(tbsData), "*"); } else { const data = JSON.parse(event.data); const cert = data.slots[0].token.certs[2].certb64; document.getElementsByName("certb64")[0].value = cert; } }
window.addEventListener("message", receiveMessage, false);</script>後端程式碼
以 Cloudflare Workers 搭配 node-forge 套件來實作驗簽。
// 本範例環境為 Cloudflare Workers// 程式碼適用於 NodeJS,需安裝 node-forge
// 以下為 Cloudflare Workers 可用的 node-forge// https://gist.github.com/chouhsiang/2dad8070e517b61cec3d175b1832c4beimport { forge } from "./node-forge.js";
export default { async fetch(request, env, ctx) { try { const { pathname } = new URL(request.url); if (pathname == "/api/pkcs1VerifyWithKey" && request.method == "POST") { return await pkcs1VerifyWithKey(request); } return new Response(""); } catch (e) { return new Response(e); } },};
async function pkcs1VerifyWithKey(request) { const formData = await request.formData();
// 載入公鑰 const begin = "-----BEGIN PUBLIC KEY-----"; const end = "-----END PUBLIC KEY-----"; const keyPem = begin + formData.get("keyb64") + end; const publicKey = forge.pki.publicKeyFromPem(keyPem);
// 將 signature 轉為 Byte const signature = formData.get("signature"); const signatureBytes = forge.util.decode64(signature);
// 將 tbs 進行 Hash const tbs = formData.get("tbs"); const plainTextBytes = forge.util.encodeUtf8(tbs); const hashAlgorithm = formData.get("hashAlgorithm").toUpperCase(); let hashCodeBytes; if (hashAlgorithm == "SHA1") { const md = forge.md.sha1.create(); md.update(plainTextBytes); hashCodeBytes = md.digest().getBytes(); } else if (hashAlgorithm == "SHA256") { const md = forge.md.sha256.create(); md.update(plainTextBytes); hashCodeBytes = md.digest().getBytes(); } else if (hashAlgorithm == "SHA384") { const md = forge.md.sha384.create(); md.update(plainTextBytes); hashCodeBytes = md.digest().getBytes(); } else if (hashAlgorithm == "SHA512") { const md = forge.md.sha512.create(); md.update(plainTextBytes); hashCodeBytes = md.digest().getBytes(); }
// 使用公鑰對 Hash、signature 進行驗證 const verified = publicKey.verify(hashCodeBytes, signatureBytes); if (verified) { return new Response("PKCS1 驗簽成功"); } else { return new Response("PKCS1 驗簽失敗"); }}