682 字
3 分鐘
[範例] 使用 node-forge 解析憑證 | 自然人憑證開發筆記
2024-01-12
無標籤

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

自然人憑證的公鑰可以解析出使用者的公鑰、姓名、身分證末四碼等資訊。


Demo#


程式碼#

Javascript 用來讀取憑證

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/forge.min.js"></script>
<h1>自然人憑證 解析憑證</h1>
<button onclick="openPopupForm(0)">讀取根憑證</button>
<button onclick="openPopupForm(1)">讀取中繼憑證</button>
<button onclick="openPopupForm(2)">讀取簽章憑證</button>
<button onclick="openPopupForm(3)">讀取加密憑證</button>
<br /><br />
憑證<br />
<textarea id="certb64" rows="8" cols="65"></textarea><br>
<button onclick="submit()">解析</button>
<hr />
<pre id="result"></pre>
let popupForm;
let timeout;
let num;
function openPopupForm(certnum) {
num = certnum;
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("certb64")[0].value = certs[num].certb64;
}
}
window.addEventListener("message", receiveMessage, false);
function submit() {
const begin = "-----BEGIN CERTIFICATE-----";
const end = "-----END CERTIFICATE-----";
// 載入憑證
const certb64 = begin + document.getElementById("certb64").value + end;
const cert = forge.pki.certificateFromPem(certb64);
// 建置回傳的JSON,toUtf8用於解決中文亂碼問題
const result = {};
result["subject"] = toUtf8(cert.subject);
result["issuer"] = toUtf8(cert.issuer);
result["validity"] = cert.validity;
result["extensions"] = cert.extensions;
result["serialNumber"] = cert.serialNumber;
result["publicKey"] = cert.publicKey;
const publicKeyPem = forge.pki.publicKeyToPem(cert.publicKey);
const publicKeyB64 = publicKeyPem
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\r\n", "");
result["publicKeyB64"] = publicKeyB64;
document.getElementById("result").innerHTML = JSON.stringify(result, null, 2);
}
// 將object中所有value轉為utf8
function toUtf8(obj) {
if (typeof obj === "object" && obj !== null) {
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj[key] = toUtf8(obj[key]);
}
}
return obj;
} else if (typeof obj === "string") {
const textDecoder = new TextDecoder("utf-8");
const byteArray = new Uint8Array([...obj].map((c) => c.charCodeAt(0)));
return textDecoder.decode(byteArray);
} else {
return obj;
}
}

Response 範例#

以簽章憑證為例,說明如下:

  • subject:本憑證資訊,個人憑證含姓名
  • issuer:上級憑證資訊
  • validity:有效期限
  • extensions:擴充欄位
    - subjectDirectoryAttributes:字串末四碼為身分證末四碼
  • publicKey:公鑰的物件
  • publicKeyB64:公鑰的 Base64,與 /pkcs11info?withcert=true 中的公鑰Base64 相同
{
"subject": {
"attributes": [
{
"type": "2.5.4.6",
"value": "TW",
"valueTagClass": 19,
"name": "countryName",
"shortName": "C"
},
{
"type": "2.5.4.3",
"value": "周詳",
"valueTagClass": 12,
"name": "commonName",
"shortName": "CN"
},
{
"type": "2.5.4.5",
"value": "00000001....",
"valueTagClass": 19,
"name": "serialNumber"
}
],
"hash": "d64b521011538e55864524a...."
},
"issuer": {
"attributes": [
{
"type": "2.5.4.6",
"value": "TW",
"valueTagClass": 19,
"name": "countryName",
"shortName": "C"
},
{
"type": "2.5.4.10",
"value": "行政院",
"valueTagClass": 12,
"name": "organizationName",
"shortName": "O"
},
{
"type": "2.5.4.11",
"value": "內政部憑證管理中心",
"valueTagClass": 12,
"name": "organizationalUnitName",
"shortName": "OU"
}
],
"hash": "25ab5e84667697ec05...."
},
"validity": {
"notBefore": "2020-11-12T01:17:56.000Z",
"notAfter": "2025-11-12T15:59:59.000Z"
},
"extensions": [
{
"id": "2.5.29.35",
"critical": false,
"value": "0\u0016€\u0014ú›4g\t\n˜\"÷bH‹‚&¦EÅÃ\"¤",
"name": "authorityKeyIdentifier"
},
{
"id": "2.5.29.14",
"critical": false,
"value": "\u0004\u0014sqdšñ)?U\u001eo\u0000é™\u0019w(¼‡‚n",
"name": "subjectKeyIdentifier",
"subjectKeyIdentifier": "7371649af1293f551e6f00e999197728bc87826e"
},
{
"id": "1.3.6.1.5.5.7.1.1",
"critical": false,
"value": "0Œ0G\u0006\b+\u0006\u0001\u0005\u0005\u00070\u0002†;http://moica.nat.gov.tw/repository/Certs/IssuedToThisCA.p7b0A\u0006\b+\u0006\u0001\u0005\u0005\u00070\u0001†5http://moica.nat.gov.tw/cgi-bin/OCSP2/ocsp_server.exe",
"name": "authorityInfoAccess"
},
{
"id": "2.5.29.15",
"critical": true,
"value": "\u0003\u0002\u0007€",
"name": "keyUsage",
"digitalSignature": true,
"nonRepudiation": false,
"keyEncipherment": false,
"dataEncipherment": false,
"keyAgreement": false,
"keyCertSign": false,
"cRLSign": false,
"encipherOnly": false,
"decipherOnly": false
},
{
"id": "2.5.29.32",
"critical": false,
"value": "0\u000b0\t\u0006\u0007`†ve\u0000\u0003\u0003",
"name": "certificatePolicies"
},
{
"id": "2.5.29.9",
"critical": false,
"value": "0*0\u0015\u0006\u0007`†v\u0001d\u0002\u00011\n\u0006\b`†v\u0001d\u0003\u0001\u00010\u0011\u0006\u0007`†v\u0001d\u000231\u0006\f\u00041234",
"name": "subjectDirectoryAttributes"
},
{
"id": "2.5.29.31",
"critical": false,
"value": "0„0@ > <†:http://moica.nat.gov.tw/repository/MOICA/CRL2/CRL_0028.crl0@ > <†:http://moica.nat.gov.tw/repository/MOICA/CRL2/complete.crl",
"name": "cRLDistributionPoints"
}
],
"serialNumber": "00d43a822d041...",
"publicKey": {
"n": {
"data": [
13863697,
47207358,
...
],
"t": 79,
"s": 0
},
"e": {
"data": [
65537
],
"t": 1,
"s": 0
}
},
"publicKeyB64": "MIIBIjANBgkqhkiG9w0BA...."
}