
上一篇已經詳細的說明 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 的內容,來判斷使用者是否有插入自然人憑證。
▶ 閱讀更多:深入淺出自然人憑證