??FIDO U2F定義了JavaScript API供開發(fā)者開發(fā)支持U2F設(shè)備的在線服務(wù)網(wǎng)站。U2F JS API分為兩類:底層基于消息端口的API和上層應(yīng)用API。在FIDO的規(guī)格文檔中介紹底層API用于與U2F設(shè)備進行消息通訊(使用MessagePort Object),發(fā)送和接收消息。本文重點關(guān)注屏蔽了通訊細節(jié)的上層API接口。
蘇州網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,蘇州網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為蘇州數(shù)千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的蘇州做網(wǎng)站的公司定做!
??使用WebIDL定義的u2f接口定義如下:
interface u2f {
void register (DOMString appId, sequence registerRequests, sequence registeredKeys, function(RegisterResponse or Error) callback, optional unsigned long? opt_timeoutSeconds);
void sign (DOMString appId, DOMString challenge, sequence registeredKeys, function(SignResponse or Error) callback, optional unsigned long? opt_timeoutSeconds);
};
??register方法中各參數(shù)描述如下:
參數(shù)名稱 | 類型 | 可否為空 | 是否可選 | 描述 |
---|---|---|---|---|
appId | DOMString | ? | ? | 請求中的應(yīng)用ID |
registerRequests | sequence |
? | ? | 注冊請求序列 |
registeredKeys | sequence |
? | ? | 已經(jīng)注冊到U2F設(shè)備的信息 |
callback | function(RegisterResponse or Error) | ? | ? | 注冊請求回調(diào)函數(shù) |
opt_timeoutSeconds | unsigned long | ? | ? | 客戶端等待請求處理的超時時間 |
??register方法成功返回的數(shù)據(jù)(callback的參數(shù))使用RegisterResponse結(jié)構(gòu)。
dictionary RegisterResponse {
DOMString version;
DOMString registrationData;
DOMString clientData;
};
??其中各屬性含義如下:
??sign方法中各參數(shù)描述如下:
參數(shù)名稱 | 類型 | 可否為空 | 是否可選 | 描述 |
---|---|---|---|---|
appId | DOMString | ? | ? | 請求中的應(yīng)用ID |
challenge | DOMString | ? | ? | 使用WEBSAFE-BASE64編碼的挑戰(zhàn)值 |
registeredKeys | sequence |
? | ? | 待簽名用戶的注冊信息 |
callback | function(SignResponse or Error) | ? | ? | 簽名請求回調(diào)函數(shù) |
opt_timeoutSeconds | unsigned long | ? | ? | 客戶端等待請求處理的超時時間 |
??sign方法成功返回的數(shù)據(jù)(callback的參數(shù))使用SignResponse結(jié)構(gòu)。
dictionary SignResponse {
DOMString keyHandle;
DOMString signatureData;
DOMString clientData;
};
??其中各屬性含義如下:
??register和sign方法失敗時返回的錯誤碼定義如下:
interface ErrorCode {
const short OK = 0;
const short OTHER_ERROR = 1;
const short BAD_REQUEST = 2;
const short CONFIGURATION_UNSUPPORTED = 3;
const short DEVICE_INELIGIBLE = 4;
const short TIMEOUT = 5;
};
??使用WebIDL定義的RegisterRequest結(jié)構(gòu)如下:
dictionary RegisterRequest {
DOMString version;
DOMString challenge;
};
屬性含義如下:
??使用WebIDL定義的RegisteredKey結(jié)構(gòu)如下:
dictionary RegisteredKey {
DOMString version;
DOMString keyHandle;
Transports? transports;
DOMString? appId;
};
各屬性含義如下:
??讓我們來到y(tǒng)ubico的U2F設(shè)備測試網(wǎng)站(https://demo.yubico.com/u2f), 使用yubico的安全key,看看register方法和sign方法如何使用。
?從淘寶上購買的兩個U2F Key,如圖1所示,其中一個飛天品牌的帶藍牙功能。
?首先測試注冊過程,如圖2所示。
?通過跟蹤網(wǎng)站客戶端與服務(wù)端的交互消息,我們發(fā)現(xiàn)開始注冊過程后,客戶端向服務(wù)端的提交了兩次請求,對應(yīng)《FIDO U2F設(shè)備應(yīng)用與開發(fā)(一)-原理與協(xié)議》第3節(jié)描述的3個階段中的第1和第3階段。
?客戶端第一次向服務(wù)端提交請求后,參數(shù)包含用戶名和口令,如圖3所示。
??在客戶端JS腳本對服務(wù)端返回的注冊請求進行register函數(shù)調(diào)用后,將register的注冊數(shù)據(jù)提交給服務(wù)端,提交的表單數(shù)據(jù)如圖4所示。
?由圖4提交的數(shù)據(jù)可以看到register返回成功,提交的表單數(shù)據(jù)為:
名稱 | 值 |
---|---|
mode | bind |
username | zhangkai |
password | zhangkai |
enroll-data | {"challenge": "OlyOzHxaxx6LUXX5chXsxj4GfspKTskBANNOtQ_UwcA", "version": "U2F_V2", "appId": "https://demo.yubico.com"} |
data(u2f設(shè)備接口調(diào)用返回數(shù)據(jù)) | {"registrationData":"BQSns2lmNJhJPSFbiDioTABT5xd2OZQpmpZFREJpbiaQC8zssXg0jLaxz8_gMioQQILSE5lsbH5BqpJwWR4rJoI1QMxn5LhVlLKhs_W-F7x4ppkw9K57h7dsTCDsikFv9BnpfSvj8XYhEHV-KEoBg8sNXq_I6-PRQ5_Z6yDkAFzrlBQwggJKMIIBMqADAgECAgQSSnL-MA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNVBAMTI1l1YmljbyBVMkYgUm9vdCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgwMTAwMDAwMFoYDzIwNTAwOTA0MDAwMDAwWjAsMSowKAYDVQQDDCFZdWJpY28gVTJGIEVFIFNlcmlhbCAyNDk0MTQ5NzIxNTgwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ9ixu9L8v2CG4QdHFgFGhIQVPBxtO0topehV5uQHV-4ivNiYi_O-_XzfIcsL9dehUNhEr-mBA8bGYH2fquKHwCozswOTAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMTATBgsrBgEEAYLlHAIBAQQEAwIFIDANBgkqhkiG9w0BAQsFAAOCAQEAoU8e6gB29rhHahCivnLmDQJxu0ZbLfv8fBvRLTUZiZFwMmMdeV0Jf6MKJqMlY06FchvC0BqGMD9rwHXlmXMZ4SIUiwSW7sjR9PlM9BEN5ibCiUQ9Hw9buyOcoT6B0dWqnfWvjjYSZHW_wjrwYoMVclJ2L_aIebzw71eNVdZ_lRtPMrY8iupbD5nGfX2BSn_1pvUt-D6JSjpdnIuC5_i8ja9MgBdf-Jcv2nkzPsRl2AbqzJSPG6siBFqVVYpIwgIm2sAD1B-8ngXqKKa7XhCkneBgoKT2omdqNNaMSr6MYYdDVbkCfoKMqeBksALWLo2M8HRJIXU9NePIfF1XeUU-dzBFAiAtXTkSxA8NFX8RU-qNtKdzBkuVSk-rIFjhkCJRALTIBwIhAKjY3XT8vJgjgyOyGhEyxGF8zQonpWvdOwFoTe77cOv-","version":"U2F_V2","challenge":"OlyOzHxaxx6LUXX5chXsxj4GfspKTskBANNOtQ_UwcA","attestation":"direct","clientData":"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6Ik9seU96SHhheHg2TFVYWDVjaFhzeGo0R2ZzcEtUc2tCQU5OT3RRX1V3Y0EiLCJvcmlnaW4iOiJodHRwczovL2RlbW8ueXViaWNvLmNvbSIsImNpZF9wdWJrZXkiOiJ1bnVzZWQifQ"} |
??注冊成功后,可執(zhí)行鑒權(quán)(login)過程,如圖5所示。
?通過跟蹤網(wǎng)站客戶端與服務(wù)端的交互消息,第一次客戶端請求時攜帶了用戶名和密碼,如圖6所示。
?在客戶端JS腳本對服務(wù)端返回的簽名請求進行sign函數(shù)調(diào)用后, U2F設(shè)備產(chǎn)生簽名后,客戶端將簽名數(shù)據(jù)提交到服務(wù)端,如圖7所示。
?提交的表單數(shù)據(jù)為:
名稱 | 值 |
---|---|
mode | verify |
rup | |
username | zhangkai |
password | zhangkai |
sign-data | {"challenge": "UGZj34u9u3KVWe3jFrcInm7ZcrPWaX_j9tohZ-34FT0", "version": "U2F_V2", "keyHandle": "3sHb84XcS8HfFaQJ_nhf4aRlWe_wYRKcg5wKelF51hOiP4iNJtGPbsfe5InJmGfoxUSjtqT46HBwG7jkFtc01Q", "appId": "https://demo.yubico.com"} |
data(u2f設(shè)備接口返回數(shù)據(jù)) | {"keyHandle":"3sHb84XcS8HfFaQJ_nhf4aRlWe_wYRKcg5wKelF51hOiP4iNJtGPbsfe5InJmGfoxUSjtqT46HBwG7jkFtc01Q","clientData":"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiVUdaajM0dTl1M0tWV2UzakZyY0lubTdaY3JQV2FYX2o5dG9oWi0zNEZUMCIsIm9yaWdpbiI6Imh0dHBzOi8vZGVtby55dWJpY28uY29tIiwiY2lkX3B1YmtleSI6InVudXNlZCJ9","signatureData":"AQAAAAEwRQIhAMCCFSBV7V8kr07XDY2bT3aPI9siDiOFdFBIm8FVTRq1AiBVaYi06GWIHw6uHE_3MFkjrbSY13k5ukPU9_xnNAo_xQ"} |
??服務(wù)端 驗證簽名后,返回驗證成功信息。
??實驗過程中,如果在交互時不按U2F設(shè)備的按鈕和不插入設(shè)備,register和sign函數(shù)都會返回錯誤碼。錯誤碼的定義可參看2.4節(jié)。
??u2f-api.js是yubico提供的U2F js api,封裝了第2節(jié)接口規(guī)范中描述的接口??蓮牡刂? https://demo.yubico.com/js/u2f-api.js 處獲取。u2f-api.js中的主要定義如下:
var u2f = u2f || {};
u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds)
u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds)
??請注意在這個腳本中將register和sign操作的超時時間定義為30秒:
u2f.EXTENSION_TIMEOUT_SEC = 30;
??在執(zhí)行3.1節(jié)的注冊過程時,通過跟蹤瀏覽器消息,可以看到第一次向服務(wù)器請求后返回的頁面中包含如下JS代碼:
setTimeout(function() {
var request = {"challenge": "OlyOzHxaxx6LUXX5chXsxj4GfspKTskBANNOtQ_UwcA", "version": "U2F_V2", "appId": "https://demo.yubico.com"};
console.log("Register: ", request);
var appId = request.appId;
var registerRequests = [{version: request.version, challenge: request.challenge, attestation: 'direct'}];
$('#promptModal').modal('show');
console.log(appId, registerRequests);
u2f.register(appId, registerRequests, [], function(data) {
console.log("Register callback", data);
$('#promptModal').modal('hide');
$('#bind-data').val(JSON.stringify(data));
$('#bind-form').submit();
});
}, 1000);
??這段代碼中,使用U2F的上層函數(shù)register進行了注冊,讀者可以與2.2節(jié)的函數(shù)參數(shù)做一下比對,在這段代碼中registeredKeys參數(shù)使用是空數(shù)組“[]”。
?仔細閱讀u2f-api.js,會發(fā)現(xiàn)腳本使用了EXTENSION_ID為“kmendfapggjehodndflmmgagdbamhnfd”的chrome內(nèi)置擴展完成與U2F設(shè)備的通訊。
?在執(zhí)行3.2節(jié)的鑒權(quán)過程時,通過跟蹤瀏覽器消息,可以看到第一次向服務(wù)器請求后返回的頁面中包含如下JS代碼:
setTimeout(function() {
var request = {"challenge": "UGZj34u9u3KVWe3jFrcInm7ZcrPWaX_j9tohZ-34FT0", "version": "U2F_V2", "keyHandle": "3sHb84XcS8HfFaQJ_nhf4aRlWe_wYRKcg5wKelF51hOiP4iNJtGPbsfe5InJmGfoxUSjtqT46HBwG7jkFtc01Q", "appId": "https://demo.yubico.com"};
console.log("sign: ", request);
var appId = request.appId;
var challenge = request.challenge;
var registeredKeys = [{version: request.version, keyHandle: request.keyHandle}];
$('#promptModal').modal('show');
u2f.sign(appId, challenge, registeredKeys, function(data) {
$('#promptModal').modal('hide');
$('#verify-data').val(JSON.stringify(data));
$('#verify-form').submit();
});
}, 1000);
??這段代碼中,使用U2F的上層函數(shù)sign進行了注冊。
??使用購買的U2F設(shè)備在PC上對chrome、firefox、IE瀏覽器進行了測試。其中chrome版本為69,firefox版本為firefox quantum 62, 這兩種瀏覽器目前都支持U2F設(shè)備。
?在firefox中使用U2F需要打開一個開關(guān)(默認(rèn)沒有打開),如圖8所示。
?這里有個有趣的問題,測試時使用的網(wǎng)站仍然是https://demo.yubico.com/u2f ,JS腳本仍然使用的是u2f-api.js,前面提到這個腳本是針對chrome內(nèi)置擴展應(yīng)用與USB口通信的,在firefox中怎么也能正常使用呢?
?原因就是firefox實現(xiàn)了自己的u2f對象,而且這個u2f對象的所有屬性都是只讀的,u2f-api.js沒有改寫這個對象,從控制臺如下輸出可以看到:
TypeError: setting getter-only property "u2f"
??IE瀏覽器使用的版本為11,不支持U2F設(shè)備。
?在安卓手機上安裝了chrome app,使用U2F設(shè)備的BLE模式(藍牙功能)測試了U2F的支持,沒有成功,在chrome調(diào)用register函數(shù)時,U2F設(shè)備沒有閃爍。