目次
はじめに

筋トレと将棋が好きな学生エンジニアのゆうき(@engieerblog_Yu)です。
電子証明書には署名用と利用者証明書用の2種類があり、pkcs11jsを使えばマイナンバーカードから利用者証明書用電子証明書が取得することができそう。
利用者証明書は「ログインした者が、利用者本人であること」を証明し、インターネットサイトなどにログインする際のセキュリティをより強固なものにできる。
今回はコマンドライン上のみの実行だが、認証サーバーを開発することをイメージしながらコーディングしている。
使用したライブラリ
PKCS11js | 2.3 |
SoftHSMv2 | 10.12.3 |
nodejs | v18.0.0 |
ライブラリのインストールはPKCS11js-GitHubを参照。
使用したカードリーダー
pkcs11jsの読み込み
pkcs11js = require("pkcs11js");
var pkcs11 = new pkcs11js.PKCS11();
pkcs11.load("/usr/local/lib/pkcs11/opensc-pkcs11.so");
pkcs11.C_Initialize();
try {
// Getting list of slots
var slots = pkcs11.C_GetSlotList(true);
var slot = slots[0];
var session = pkcs11.C_OpenSession(slot, pkcs11js.CKF_RW_SESSION | pkcs11js.CKF_SERIAL_SESSION);
pkcs11.C_Login(session, 1, "my_number_password");
//この中にマイナンバー認証するためのコードを記載していく
pkcs11.C_Logout(session);
pkcs11.C_CloseSession(session);
}
catch(e){
console.error(e);
}
finally {
pkcs11.C_Finalize();
}
my_number_passwordはマイナンバーカード発行時に作成した4桁のパスワード。
認証用のチャレンジデータを作成する(base64エンコード)
// 認証のチャレンジデータを作成
const crypto = require('crypto');
const N = 16;
const challenge = crypto.randomBytes(N).toString('base64').substring(0, N);
カード内の秘密鍵ハンドルでチャレンジデータに署名
// 秘密鍵ハンドルの取得
pkcs11.C_FindObjectsInit(session, [{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_PRIVATE_KEY }]);
var hObject = pkcs11.C_FindObjects(session);
while (hObject) {
var attrs2 = pkcs11.C_GetAttributeValue(session, hObject, [
{ type: pkcs11js.CKA_CLASS },
{ type: pkcs11js.CKA_TOKEN },
{ type: pkcs11js.CKA_LABEL }
]);
// Output info for objects from token only
if (attrs2[1].value[0]){
console.log(`Object #${hObject}: ${attrs2[2].value.toString()}`);
}
privateKey = hObject;
hObject = pkcs11.C_FindObjects(session);
}
pkcs11.C_FindObjectsFinal(session);
// 秘密鍵を使った署名
pkcs11.C_SignInit(session, { mechanism: pkcs11js.CKM_SHA256_RSA_PKCS }, privateKey);
pkcs11.C_SignUpdate(session, Buffer.from(challenge));
var signature = pkcs11.C_SignFinal(session, Buffer.alloc(256));
カードから利用者証明書を取得
// 利用者証明書の取得
pkcs11.C_FindObjectsInit(session, [{ type: pkcs11js.CKA_CLASS, value: pkcs11js.CKO_CERTIFICATE }, { type: pkcs11js.CKA_LABEL, value: "User Authentication Certificate" }]);
var hObject = pkcs11.C_FindObjects(session);
while (hObject) {
var attrs1 = pkcs11.C_GetAttributeValue(session, hObject, [
{ type: pkcs11js.CKA_CLASS },
{ type: pkcs11js.CKA_TOKEN },
{ type: pkcs11js.CKA_LABEL },
{ type: pkcs11js.CKA_VALUE }
]);
// Output info for objects from token only
if (attrs1[1].value[0]){
console.log(`Object #${hObject}: ${attrs1[2].value.toString()}`);
}
hObject = pkcs11.C_FindObjects(session);
}
pkcs11.C_FindObjectsFinal(session);
const { X509Certificate } = require('crypto');
const x509 = new X509Certificate(attrs1[3].value);
利用者証明書の公開鍵で署名を検証する
// 署名したものをx509の公開鍵で検証
publicKey = x509.publicKey;
const {createVerify} = require('crypto');
const verify = createVerify('RSA-SHA256');
verify.update(challenge);
verify.end();
console.log("検証結果:"+verify.verify(publicKey, signature));
x509の公開鍵を使って検証するとtrueが表示される。
検証結果:true
verify.update()の引数を変えるともちろんfalseになる。
利用者証明者の属性を印字する
// 利用者証明書の属性を印字
console.log("[subject]\n"+x509.subject);
console.log("[validFrom]\n"+x509.validFrom);
console.log("[validTo]\n"+x509.validTo);
console.log("[issuer]\n"+x509.issuer);
マイナンバーカードの利用者証明書を取得できていることが確認できる。
Qiitaでより詳細をまとめました。
Nodejsでマイナンバーカードを使ったログインと認証をやってみる(PKCS11js,X509,OpenSC,crypto)
コメント