882 字
4 分鐘
[範例] ChtICToken.js 進行數位簽章 | 自然人憑證開發筆記
2024-01-05
無標籤

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

上一篇透過 JavaScript 做數位簽章,本篇透過 ChtICToken.js 來實作一樣的功能。


範例程式碼#

範例程式功能如下,輸入演算法、TBS、PIN等資訊進行簽章。關於 ChtICToken.js 的使用,請參考前篇說明,主要重點在 icToken.sign 的使用。

<h1>自然人憑證 數位簽章</h1>
signatureType: <select id="signatureType">
<option value="PKCS1" selected>PKCS1</option>
<option value="PKCS7">PKCS7</option>
<option value="RAW">RAW</option>
</select><br>
tbs: <input type="text" id="tbs" /><br>
pin: <input type="text" id="pin" /><br>
hashAlgorithm: <select id="hashAlgorithm">
<option value="SHA1">SHA1</option>
<option value="SHA256" selected>SHA256</option>
<option value="SHA384">SHA384</option>
<option value="SHA512">SHA512</option>
</select><br><br>
<b>PKCS7 專用</b><br>
nonce: <input type="text" id="nonce" /><br>
withCardSN: <select id="withCardSN">
<option value="false" selected>false</option>
<option value="true">true</option>
</select><br>
<input type="button" value="簽章" onclick="getICToken().goodDay(sign)"><br>
<hr>
cardSN (卡號): <input type="text" id="cardSN" /><br>
ret_code (結果): <input type="text" id="ret_code" /><br>
signature (簽章值)<br>
<textarea id="certb64" rows="8" cols="65"></textarea><br>
<div id="info"></div>
<script src="https://fido.moi.gov.tw/pt/assets/ChtICToken.js"></script>
<script>
function setData() {
const icToken = getICToken();
document.getElementById("info").innerHTML = JSON.stringify(icToken);
document.getElementById("cardSN").value = icToken.SmrtCrdID[0];
document.getElementById("ret_code").value = icToken.RetObj.RCode;
document.getElementById("signature").value = icToken.RetObj.B64Signature;
}
function sign() {
const icToken = getICToken();
const tbs = btoa(document.getElementById("tbs").value);
const pin = document.getElementById("pin").value;
const hashAlgorithm = document.getElementById("hashAlgorithm").value;
const signatureType = document.getElementById("signatureType").value;
const keyidb64 = btoa("KEYX");
const nonce = document.getElementById("nonce").value;
const withSigningTime = "true";
const withCardSN = document.getElementById("withCardSN").value;
const checkValidity = "true";
icToken.sign(tbs, pin, hashAlgorithm, setData, signatureType, keyidb64, nonce, withSigningTime, withCardSN, checkValidity);
}
</script>

sign(B64ToBeSign, UserPIN, HashAlg, RevFun, PKCSSignType, keyidb64, nonce, withSigningTime, withCardSN, checkValidity)#

sign 用於簽章,背後是呼叫API http://127.0.0.1:61161/sign,先了解前篇關於 /sign 的說明。接著來說明參數:

  • B64ToBeSign:等同 /sign 的 tbs 欄位,為 Base64 格式,tbsEncoding=base64 會被固定啟用。
  • UserPIN:等同 /sign 的 pin 欄位
  • RevFun:Callback Function
  • keyidb64:等同 /sign 的 keyidb64 欄位
  • nonce:等同 /sign 的 nonce欄位
  • withSigningTime:等同 /sign 的 withSigningTime欄位
  • withCardSN:等同 /sign 的 withCardSN欄位
  • checkValidity:等同 /sign 的 checkValidity欄位

以下是 sign 的原始碼

ICToken.prototype.sign = function (
B64ToBeSign, UserPIN, HashAlg, RevFun, PKCSSignType,
keyidb64, nonce, withSigningTime, withCardSN, checkValidity
) {
if (typeof PKCSSignType === "undefined") PKCSSignType = "PKCS1";
if (typeof keyidb64 === "undefined") keyidb64 = "__NotExistedTag__";
if (typeof nonce === "undefined") nonce = "__NotExistedTag__";
if (typeof withSigningTime === "undefined")
withSigningTime = "__NotExistedTag__";
if (typeof withCardSN === "undefined") withCardSN = "__NotExistedTag__";
if (typeof checkValidity === "undefined") checkValidity = "__NotExistedTag__";
UserSign(
B64ToBeSign, encodeURIComponent(UserPIN), HashAlg, RevFun, PKCSSignType,
keyidb64, encodeURIComponent(nonce), encodeURIComponent(withSigningTime),
encodeURIComponent(withCardSN), encodeURIComponent(checkValidity), "none"
);
};
function UserSign(
B64ToBeSign, UserPIN, HashAlg, UrRevFUN, PKCSSignType, keyidb64,
nonce, withSigningTime, withCardSN, checkValidity, SignType
) {
var l_iRetCode = 0;
var l_B64Signature = "";
var l_vRMsg;
var l_vRetData;
if (typeof PKCSSignType === "undefined") PKCSSignType = "PKCS1";
if (typeof keyidb64 === "undefined") keyidb64 = "__NotExistedTag__";
if (typeof nonce === "undefined") nonce = "__NotExistedTag__";
if (typeof withSigningTime === "undefined")
withSigningTime = "__NotExistedTag__";
if (typeof withCardSN === "undefined") withCardSN = "__NotExistedTag__";
if (typeof checkValidity === "undefined") checkValidity = "__NotExistedTag__";
if (PKCSSignType == "PKCS7") {
InToken.SType = "PKCS7";
} else {
//Default: "PKCS1"
}
if (SignType.localeCompare("Cryp") == 0) {
InToken.FUN = "umakeSig";
GARevFun.FUN = "StSgn";
} else {
InToken.FUN = "MakeSignature";
GARevFun.FUN = "StSgn";
}
InToken.PIN = UserPIN;
InToken.TBS = B64ToBeSign;
InToken.keyidb64 = keyidb64;
InToken.nonce = nonce;
InToken.withSigningTime = withSigningTime;
InToken.withCardSN = withCardSN;
InToken.checkValidity = checkValidity;
GARevFun.Para = HashAlg.toUpperCase();
GARevFun.UrFUN = UrRevFUN;
switch (GARevFun.Para) {
case "SHA256":
InToken.HashAlgorithm = "SHA256";
break;
case "SHA1":
InToken.HashAlgorithm = "SHA1";
break;
default:
l_iRetCode = 3400;
l_vRMsg = rMsg(l_iRetCode, "");
break;
}
if (B64ToBeSign == null) {
l_iRetCode = 3402;
l_vRMsg = rMsg(l_iRetCode, "");
}
if (UserPIN == null) {
l_iRetCode = 3403;
l_vRMsg = rMsg(l_iRetCode, "");
}
if (HashAlg == null) {
l_iRetCode = 3404;
l_vRMsg = rMsg(l_iRetCode, "");
}
if (UrRevFUN == null) {
l_iRetCode = 3405;
l_vRMsg = rMsg(l_iRetCode, "");
alert("輸入參數錯誤或不足");
}
if (checkGoodDay() == false) {
l_iRetCode = 3401;
l_vRMsg = rMsg(l_iRetCode, "");
}
if (l_iRetCode == 0) {
if (SignType.localeCompare("Cryp") == 0) {
SendData(
"http://localhost:61161/umakeSig",
"http://localhost:61161/ChtPopupForm",
"IC卡讀取中",
Gbjct.PppFrmStyle,
StSgn,
UrRevFUN
);
} else {
SendData(
"http://localhost:61161/sign",
"http://localhost:61161/ChtPopupForm",
"IC卡讀取中",
Gbjct.PppFrmStyle,
StSgn,
UrRevFUN
);
}
} else {
l_vRetData =
'{"RCode":"' +
l_iRetCode +
'", "B64Signature":"' +
l_B64Signature +
'","RMsg":"' +
l_vRMsg +
'"}';
InToken.RetObj = JSON.parse(l_vRetData);
if (UrRevFUN && typeof UrRevFUN === "function") UrRevFUN();
}
}

以下是簽章回傳結果的格式

{
"SmrtCrdID": ["<卡號>"],
"SlotID": ["InfoThink USB Reader 0"],
"SlotName": [],
"ActvSltID": ["0"],
"OldPIN": "",
"NewPIN": "",
"UID": "",
"CrdSys": "",
"KpMsg": "",
"exDynamicCreateFileInput": "",
"exDynamicReleaseFileInput": "",
"TBS": "VEJT",
"TbsEncoding": "base64",
"HashAlgorithm": "SHA256",
"PIN": "000000",
"FUN": "MakeSignature",
"SType": "PKCS1",
"RetObj": {
"RCode": "0",
"B64Signature": "<簽章值>",
"RMsg": "undefined"
},
"keyidb64": "__NotExistedTag__",
"nonce": "__NotExistedTag__",
"withSigningTime": "__NotExistedTag__",
"withCardSN": "__NotExistedTag__",
"checkValidity": "__NotExistedTag__"
}

結語#

透過 ChtICToken.js 也可以進行簽章,不過與原生 API 差別在於,ChtICToken.js 不會回傳公鑰,要另外取得。

▶ 閱讀更多:深入淺出自然人憑證