537 字
3 分鐘
[範例] node-forge 驗證憑證鏈 | 自然人憑證開發筆記
2024-01-11
無標籤

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

前文有介紹到,自然人憑證卡裡面有四張憑證,包含政府根憑證、內政部中繼憑證、兩張個人終端憑證(簽章、加密),本文將說明如何透過後端對終端憑證做憑證鏈驗證。


Demo#


前端程式碼#

前文學習到如何讀取自然人憑證裡面的四張憑證,範例為讀取憑證後,按下驗證按鈕進行驗證憑證鏈。

<h1>自然人憑證 憑證鏈驗證</h1>
<input type="button" value="讀取簽章憑證" onclick="getCert(true)" />
<input type="button" value="讀取加密憑證" onclick="getCert(false)" />
<form action="https://cert.modatw.workers.dev/api/certChainVerify" method="post">
根憑證<br>
<textarea name="cert1b64" rows="8" cols="65"></textarea><br>
中繼憑證<br>
<textarea name="cert2b64" rows="8" cols="65"></textarea><br>
終端憑證<br>
<textarea name="cert3b64" rows="8" cols="65"></textarea><br>
<input value="驗證" type="submit">
</form>
<script>
let popupForm;
let timeout;
let cert;
function getCert(c) {
cert = c;
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 certs = data.slots[0].token.certs;
document.getElementsByName("cert1b64")[0].value = certs[0].certb64;
document.getElementsByName("cert2b64")[0].value = certs[1].certb64;
document.getElementsByName("cert3b64")[0].value = cert
? certs[2].certb64
: certs[3].certb64;
}
}
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/certChainVerify" && request.method == "POST") {
return await certChainVerify(request);
}
return new Response("");
} catch (e) {
return new Response(e);
}
},
};
async function certChainVerify(request) {
const formData = await request.formData();
const begin = "-----BEGIN CERTIFICATE-----";
const end = "-----END CERTIFICATE-----";
// 將憑證轉為 PEM 格式
const rootCACert = begin + formData.get("cert1b64") + end;
const intermediateCert = begin + formData.get("cert2b64") + end;
const endEntityCert = begin + formData.get("cert3b64") + end;
// 解析 PEM
const rootCACertificate = forge.pki.certificateFromPem(rootCACert);
const intermediateCertificate = forge.pki.certificateFromPem(intermediateCert);
const endEntityCertificate = forge.pki.certificateFromPem(endEntityCert);
// 建立 CA 儲存庫,加入根憑證、中繼憑證
const caStore = forge.pki.createCaStore([rootCACertificate, intermediateCertificate]);
// 驗證憑證
try {
forge.pki.verifyCertificateChain(caStore, [endEntityCertificate]);
return new Response("成功");
} catch (e) {
return new Response("失敗," + JSON.stringify(e));
}
}