1276 字
6 分鐘
[範例] ChtICToken.js 取得憑證 | 自然人憑證開發筆記
2024-01-03
無標籤

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

上一篇講到如何透過 postMessage 、popupForm 跟 HiPKI Local Server 溝通。在第二篇有提到,Hisecure 有幫 HiPKI Local Server 開發 JavaScript 的函式庫 ChtICToken.js,本篇將說明如何透過 ChtICToken.js 來達成取得使用者資訊與公鑰。


範例程式碼#

範例程式功能如下,有一個讀取基本資訊的按鈕,可以取得卡號。由於 ChtICToken.js 設計,要讀取憑證 Base64、憑證資訊是不同功能,因次做了兩個按鈕。讀取憑證的時候,需先選擇要讀取哪一張憑證。

<input value="讀取基本資訊" onclick="getICToken().goodDay(setData)" type="button"/>
<br><br>
<select id="crtId">
<option value="ROOTCACert" selected>根憑證</option>
<option value="CACert">中繼憑證</option>
<option value="SIGN">簽章憑證</option>
<option value="KEYX">加密憑證</option>
</select>
<input value="讀取憑證Base64" onclick="getICToken().goodDay(getB64Certificate)" type="button"/>
<input value="讀取憑證資訊" onclick="getICToken().goodDay(parseCertificate)" type="button"/>
<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);
}
function getB64Certificate() {
const icToken = getICToken();
const crtId = btoa(document.getElementById("crtId").value);
icToken.getB64Certificate(0, setData, crtId);
}
function parseCertificate() {
const icToken = getICToken();
const crtId = btoa(document.getElementById("crtId").value);
icToken.parseCertificate(0, setData, crtId);
}
</script>

JavaScript Callback Function#

在 JavaScript 中,將一個 function 作為另一個 function 的參數傳遞是一種高階 function 的概念。這種技術稱為 Callback Function。這是 JavaScrip t中的一個強大的功能,它使得可以更靈活地處理函數,使其能夠動態地適應不同的情境和需求。

以下是一個簡單的例子,展示了如何將 function 作為參數傳遞:

function doSomething(callback) {
// 執行一些操作
console.log("Doing something...");
// 調用回調函數
callback();
}
function callbackFunction() {
console.log("Callback function executed!");
}
// 將callbackFunction作為參數傳遞給doSomething函數
doSomething(callbackFunction);

在這個例子中,doSomething 函數接受一個 Callback Function 作為參數,然後在 function 內執行一些操作後,調用該 Callback Function。這種方式使得程式碼更具彈性,因為可以根據需要動態地指定不同的 Callback Function。


ChtICToken.js 說明#

ChtICToken.js 使用 Callback Function,將執行完畢後要執行的程式碼定義為 Callback Function,並放到參數中。

先從讀取基本資訊說明,先呼叫 getICToken().goodDay(setData)。goodDay 代表初始化,開始讀取自然人憑證。ChtICToken.js 取得資料後就會觸發 setData function。setData 則將拿到的資料顯示在網頁上。

<input value="讀取基本資訊" onclick="getICToken().goodDay(setData)" type="button" />
<div id="info"></div>
<script>
function setData() {
const icToken = getICToken();
document.getElementById("info").innerHTML = JSON.stringify(icToken);
}
</script>

得到的資料會長這樣,SmrtCrdID 是卡號;SlotID 是讀卡機名稱;RetObj.RCode 是執行結果,0 代表成功。

{
"SmrtCrdID": ["TP03200302000000"],
"SlotID": ["InfoThink USB Reader 0"],
"SlotName": [],
"ActvSltID": ["0"],
"OldPIN": "",
"NewPIN": "",
"UID": "",
"CrdSys": "",
"KpMsg": "",
"exDynamicCreateFileInput": "",
"exDynamicReleaseFileInput": "",
"TBS": "TBS",
"TbsEncoding": "base64",
"HashAlgorithm": "SAH256",
"PIN": "",
"FUN": "CheckEnvir",
"SType": "PKCS1",
"RetObj": {
"RCode": "0",
"RMsg": ""
}
}

取得憑證資訊#

接著來分析取得憑證的 Base64、資訊。這裡做了兩次 Callback Function,讀取憑證Base64,goodDay 初始化後,Callback 到 getB64Certificate Function。透過 icToken.getB64Certificate 取得憑證 Base64 後,再 Callback 到 setData,將結果顯示到網站上。parseCertificate 原理相同,用來取得憑證資訊。

第四篇有介紹自然人憑證內四張憑證,包含根憑證(id=ROOTCACert)、中繼憑證(id=CACert)、簽章憑證(id=SIGN)、加密憑證(id=KEYX)。

<select id="crtId">
<option value="ROOTCACert" selected>根憑證</option>
<option value="CACert">中繼憑證</option>
<option value="SIGN">簽章憑證</option>
<option value="KEYX">加密憑證</option>
</select>
<input value="讀取憑證Base64" onclick="getICToken().goodDay(getB64Certificate)" type="button" />
<input value="讀取憑證資訊" onclick="getICToken().goodDay(parseCertificate)" type="button" />
<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);
}
function getB64Certificate() {
const icToken = getICToken();
const crtId = btoa(document.getElementById("crtId").value);
icToken.getB64Certificate(0, setData, crtId);
}
function parseCertificate() {
const icToken = getICToken();
const crtId = btoa(document.getElementById("crtId").value);
icToken.parseCertificate(0, setData, crtId);
}
</script>

parseCertificate(index, RevFun, keyidb64)#

parseCertificate 用於取得憑證資訊,原理與 getB64Certificate 大致相同,參數也一樣。以下為 parseCertificate 原始碼。

  • index:憑證選擇,0 代表從 keyidb64 找到對應 id 的憑證,1 代表簽章憑證,2 代表加密憑證
  • RevFun:Callback Function
  • keyidb64:當 index=0,用來選擇憑證的 id,採用 Base64 格式
ICToken.prototype.parseCertificate = function (index, RevFun, keyidb64) {
if (typeof keyidb64 === "undefined") keyidb64 = "__NotExistedTag__";
UsrPrsCrt(index, RevFun, keyidb64);
};
function UsrPrsCrt(index, UrRevFUN, keyidb64) {
if (typeof keyidb64 === "undefined") keyidb64 = "__NotExistedTag__";
var l_iRetCode = 0;
var l_CertInfo = "";
var l_vExp;
var l_vRetData;
var l_vTemp;
var l_vRMsg;
l_vRMsg = "";
InToken.FUN = "GetUserCert";
GARevFun.FUN = "PrsCrt";
GARevFun.Para = index;
GARevFun.keyidb64 = keyidb64;
GARevFun.UrFUN = UrRevFUN;
switch (index) {
case 0:
l_vExp = reg(GARevFun.keyidb64);
break;
case 1:
l_vExp = /digitalSignature/i;
break;
case 2:
l_vExp = /keyEncipherment|dataEncipherment/i;
break;
default:
l_iRetCode = 3800;
l_vRMsg = rMsg(l_iRetCode, "");
break;
}
if (UrRevFUN == null) {
l_iRetCode = 3802;
l_vRMsg = rMsg(l_iRetCode, "");
alert("輸入參數錯誤或不足");
}
if (checkGoodDay() == false) {
l_iRetCode = 3801;
l_vRMsg = rMsg(l_iRetCode, "");
}
if (l_iRetCode == 0) {
SendData(
"http://localhost:61161/pkcs11info?withcert=true",
"http://localhost:61161/ChtPopupForm",
"IC卡讀取中",
Gbjct.PppFrmStyle,
PrsCrt,
UrRevFUN
);
} else {
l_vRetData = '{"RCode":"' + l_iRetCode + '","RMsg":"' + l_vRMsg + '"}';
InToken.RetObj = JSON.parse(l_vRetData);
if (UrRevFUN && typeof UrRevFUN === "function") UrRevFUN();
}
}

以下是根憑證的回傳結果

{
"SmrtCrdID": ["TP03200302000000"],
"SlotID": ["InfoThink USB Reader 0"],
"SlotName": [],
"ActvSltID": ["0"],
"OldPIN": "",
"NewPIN": "",
"UID": "",
"CrdSys": "",
"KpMsg": "",
"exDynamicCreateFileInput": "",
"exDynamicReleaseFileInput": "",
"TBS": "TBS",
"TbsEncoding": "base64",
"HashAlgorithm": "SAH256",
"PIN": "",
"FUN": "GetUserCert",
"SType": "PKCS1",
"RetObj": {
"RCode": "0",
"RMsg": "",
"NotAfterYear": "2032",
"NotAfterMonth": "12",
"NotAfterDate": "5",
"NotBeforeYear": "2002",
"NotBeforeMonth": "12",
"NotBeforeDate": "5",
"Subject": "C=TW,O=Government Root Certification Authority",
"CertSN": "1F9D595AD72FC20644A5800869E35EF6",
"IssuerDN": "C=TW,O=Government Root Certification Authority",
"KeyUsage": "undefined",
"SubjectID": "undefined",
"CardRank": "undefined"
}
}

結語#

透過 Javascript 呼叫 popupForm 或是 ChtICToken.js,都可以取得使用者公鑰與資訊,而且不需要 pin 碼,ChtICToken.js 使用上會比較簡單,把 popupForm 與其他語法都打包起來了。

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