685 字
3 分鐘
[範例] 解碼 p11Image.bmp 取得讀卡機資訊 | 自然人憑證開發筆記
2023-12-31
無標籤

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

上一篇已經詳細的說明 p11Image.bmp 的原理與圖像隱寫術機制,本篇來接著講如何透過 JavaScript 來判斷使用者是否有插著卡。


Demo#


程式碼#

我研究了內政部範例網站行動自然人憑證網站的 JavaScript 寫法,並簡化了語法,寫成以下範例,會直接在網頁上顯示解析完 p11Image.bmp 的JSON內容。可以到 Codepen 上玩玩看。


程式碼說明#

來逐行說明上面程式的內容。

步驟一、宣告 Image#

const img = new Image();
img.crossOrigin = "Anonymous";
img.src = 'http://localhost:61161/p11Image.bmp';

先宣告一個 Image 物件,並設定路徑 src 為 p11Image.bmp,設定 crossOrigin 為 Anonymous,需要設定 Anonymous 原因為允許跨網域訪問,但不需要使用驗證(例如 Cookie 或 HTTP 身份驗證)。代表它使用匿名身份進行跨來源請求,這有助於解決 CORS 限制。

步驟二、將 Image 塞入 Canvas 畫布#

const canvas = document.createElement("canvas");
canvas.width = 2000;
canvas.height = 1;
const ctx = canvas.getContext('2d');
img.onload = function() {
ctx.drawImage(img, 0, 0);
document.getElementById('info').innerHTML = getImageInfo(ctx);
};

由於需要讀取圖片中的像素,我們需要將 Image 轉為 Canvas,需要先宣告一個 Canvas 物件,並設定長寬為 2000 x 1的圖片,ctx 則是取得 Canvas 2D 渲染的環境。
接著設定 img.onload,代表圖片載入完成後觸發,將 Image 塞入 Canvas 畫布中,下一步要透過 getImageInfo 將 ctx 轉為文字。

步驟三、getImageInfo 將 ctx 轉為文字#

function getImageInfo(ctx) {
let output = "";
for (let i = 0; i < 2000; i++) {
const data = ctx.getImageData(i, 0, 1, 1).data;
if (data[2] === 0) {
break;
}
output += String.fromCharCode(data[2], data[1], data[0]);
}
return output;
}

在 Canvas 中可以透過 getImageData 取得像素資訊,getImageData(0, 0, 1, 1) 代表 (x, y) 座標為 (0, 0),並取得長寬 1 x 1 的像素資訊。所以這裡我們寫個迴圈讀取座標 (0,0) (1,0) (2,0) …,一開始我們 canvas 宣告是 2000 x 1的畫布,那這裡迴圈就跑 2000 次。

getImageData 讀出來的 data 會是一個 Uint8ClampedArray,data[0] 代表 RGB 中的紅色,data[1] 代表 RGB 中的綠色,data[2] 代表 RGB 中的藍色,讀出來會是 0–255 之間的數字。

接著依 BGR 順序排序,透過 String.fromCharCode 將 ASCII 轉為文字,並放到 output 字串,例如第一個像素是 rgb(67, 66, 65),就可以轉換成文字 ABC。當 data[2] 抓到的值是 0,代表資料已經讀取完了,就跳脫迴圈。


結語#

透過這種神奇的機制,就可以把圖片轉為文字,並且讓前端可以識別內容。開發者可以透過檢查 slots 的內容,來判斷使用者是否有插入自然人憑證。

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