495 字
2 分鐘
[範例] node-forge 憑證驗證 PKCS1 簽章值 | 自然人憑證開發筆記
2024-01-11
無標籤

首頁 > 系列文 > 自然人憑證開發筆記

前文說明過如何透過前端做 PKCS1 的簽章,本文將說明如何為 PKCS1 簽章值進行驗簽。


Demo#


前端程式碼#

驗證 PKCS1 時,我們需要 TBS、Hash 演算法、憑證、簽章值,Javascript 用來讀取簽章憑證。

<h1>自然人憑證 憑證驗簽PKCS1</h1>
<input type="button" value="讀取簽章憑證" onclick="getCert()" />
<form action="https://cert.modatw.workers.dev/api/pkcs1VerifyWithCert" 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>
certb64 (憑證)<br>
<textarea name="certb64" 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/2dad8070e517b61cec3d175b1832c4be
import { forge } from "./node-forge.js";
export default {
async fetch(request, env, ctx) {
try {
const { pathname } = new URL(request.url);
if (pathname == "/api/pkcs1VerifyWithCert" && request.method == "POST") {
return await pkcs1VerifyWithCert(request);
}
return new Response("");
} catch (e) {
return new Response(e);
}
},
};
async function pkcs1VerifyWithCert(request) {
const formData = await request.formData();
// 從憑證提取公鑰
const begin = "-----BEGIN CERTIFICATE-----";
const end = "-----END CERTIFICATE-----";
const certPem = begin + formData.get("certb64") + end;
const cert = forge.pki.certificateFromPem(certPem);
const publicKey = cert.publicKey;
// 將簽章值轉為 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 驗簽失敗");
}
}