千家信息网

FIDO U2F应用开发(二)-编程接口

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,1. U2F JS API  FIDO U2F定义了JavaScript API供开发者开发支持U2F设备的在线服务网站。U2F JS API分为两类:底层基于消息端口的API和上层应用API。在FI
千家信息网最后更新 2026年11月01日FIDO U2F应用开发(二)-编程接口

1. U2F JS API

  FIDO U2F定义了JavaScript API供开发者开发支持U2F设备的在线服务网站。U2F JS API分为两类:底层基于消息端口的API和上层应用API。在FIDO的规格文档中介绍底层API用于与U2F设备进行消息通讯(使用MessagePort Object),发送和接收消息。本文重点关注屏蔽了通讯细节的上层API接口。

2. 接口定义

2.1. u2f接口

  使用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);};

2.2. register方法

2.2.1. 请求参数

  register方法中各参数描述如下:

参数名称类型可否为空是否可选描述
appIdDOMString✘✘请求中的应用ID
registerRequestssequence✘✘注册请求序列
registeredKeyssequence✘✘已经注册到U2F设备的信息
callbackfunction(RegisterResponse or Error)✘✘注册请求回调函数
opt_timeoutSecondsunsigned long✔✔客户端等待请求处理的超时时间

2.2.2. 返回值

  register方法成功返回的数据(callback的参数)使用RegisterResponse结构。

dictionary RegisterResponse {    DOMString version;    DOMString registrationData;    DOMString clientData;};

  其中各属性含义如下:

  • version:U2F协议版本,如"U2F_V2"
  • registrationData:使用websafe-base64编码后的注册数据,数据格式参看《FIDO U2F设备应用与开发(一)-原理与协议》3.2节。
  • clientData:使用websafe-base64编码后的clientData,数据格式参看《FIDO U2F设备应用与开发(一)-原理与协议》3.5节。

    2.3. sign方法

    2.3.1. 请求参数

      sign方法中各参数描述如下:

参数名称类型可否为空是否可选描述
appIdDOMString✘✘请求中的应用ID
challengeDOMString✘✘使用WEBSAFE-BASE64编码的挑战值
registeredKeyssequence✘✘待签名用户的注册信息
callbackfunction(SignResponse or Error)✘✘签名请求回调函数
opt_timeoutSecondsunsigned long✔✔客户端等待请求处理的超时时间

2.3.2. 返回值

  sign方法成功返回的数据(callback的参数)使用SignResponse结构。

dictionary SignResponse {    DOMString keyHandle;    DOMString signatureData;    DOMString clientData;};

  其中各属性含义如下:

  • keyHandle:请求中提供的key handle
  • signatureData:使用websafe-base64编码后的签名数据,数据格式参看《FIDO U2F设备应用与开发(一)-原理与协议》3.4节。
  • clientData:使用websafe-base64编码后的clientData,数据格式参看《FIDO U2F设备应用与开发(一)-原理与协议》3.5节。

    2.4. 错误码

      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;};

    2.5. 接口中的数据结构

    2.5.1. RegisterRequest

      使用WebIDL定义的RegisterRequest结构如下:

    dictionary RegisterRequest {DOMString version;DOMString challenge;};

    属性含义如下:

  • version:U2F协议版本,如"U2F_V2"
  • challenge:使用websafe-base64编码的挑战值

2.5.2. RegisteredKey

  使用WebIDL定义的RegisteredKey结构如下:

dictionary RegisteredKey {    DOMString   version;    DOMString   keyHandle;    Transports? transports;    DOMString?  appId;};

各属性含义如下:

  • version:U2F协议版本,如"U2F_V2"
  • keyHandle:用于签名用户的key handle
  • transports:传输方式,可选参数
  • appId:在线服务网站应用Id

3. 编程接口实例探究

3.1. 注册过程

  让我们来到yubico的U2F设备测试网站(https://demo.yubico.com/u2f), 使用yubico的安全key,看看register方法和sign方法如何使用。
  从淘宝上购买的两个U2F Key,如图1所示,其中一个飞天品牌的带蓝牙功能。

  首先测试注册过程,如图2所示。

  通过跟踪网站客户端与服务端的交互消息,我们发现开始注册过程后,客户端向服务端的提交了两次请求,对应《FIDO U2F设备应用与开发(一)-原理与协议》第3节描述的3个阶段中的第1和第3阶段。
  客户端第一次向服务端提交请求后,参数包含用户名和口令,如图3所示。

  在客户端JS脚本对服务端返回的注册请求进行register函数调用后,将register的注册数据提交给服务端,提交的表单数据如图4所示。

  由图4提交的数据可以看到register返回成功,提交的表单数据为:

名称值
modebind
usernamezhangkai
passwordzhangkai
enroll-data{"challenge": "OlyOzHxaxx6LUXX5chXsxj4GfspKTskBANNOtQ_UwcA", "version": "U2F_V2", "appId": "https://demo.yubico.com"}
data(u2f设备接口调用返回数据){"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"}

3.2. 鉴权过程

  注册成功后,可执行鉴权(login)过程,如图5所示。

  通过跟踪网站客户端与服务端的交互消息,第一次客户端请求时携带了用户名和密码,如图6所示。

  在客户端JS脚本对服务端返回的签名请求进行sign函数调用后, U2F设备产生签名后,客户端将签名数据提交到服务端,如图7所示。

  提交的表单数据为:

名称值
modeverify
rup
usernamezhangkai
passwordzhangkai
sign-data{"challenge": "UGZj34u9u3KVWe3jFrcInm7ZcrPWaX_j9tohZ-34FT0", "version": "U2F_V2", "keyHandle": "3sHb84XcS8HfFaQJ_nhf4aRlWe_wYRKcg5wKelF51hOiP4iNJtGPbsfe5InJmGfoxUSjtqT46HBwG7jkFtc01Q", "appId": "https://demo.yubico.com"}
data(u2f设备接口返回数据){"keyHandle":"3sHb84XcS8HfFaQJ_nhf4aRlWe_wYRKcg5wKelF51hOiP4iNJtGPbsfe5InJmGfoxUSjtqT46HBwG7jkFtc01Q","clientData":"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiVUdaajM0dTl1M0tWV2UzakZyY0lubTdaY3JQV2FYX2o5dG9oWi0zNEZUMCIsIm9yaWdpbiI6Imh0dHBzOi8vZGVtby55dWJpY28uY29tIiwiY2lkX3B1YmtleSI6InVudXNlZCJ9","signatureData":"AQAAAAEwRQIhAMCCFSBV7V8kr07XDY2bT3aPI9siDiOFdFBIm8FVTRq1AiBVaYi06GWIHw6uHE_3MFkjrbSY13k5ukPU9_xnNAo_xQ"}

  服务端 验证签名后,返回验证成功信息。

3.3. 异常处理

  实验过程中,如果在交互时不按U2F设备的按钮和不插入设备,register和sign函数都会返回错误码。错误码的定义可参看2.4节。

3.4. u2f-api.js

  u2f-api.js是yubico提供的U2F js api,封装了第2节接口规范中描述的接口。可从地址: 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;

  在执行3.1节的注册过程时,通过跟踪浏览器消息,可以看到第一次向服务器请求后返回的页面中包含如下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的上层函数register进行了注册,读者可以与2.2节的函数参数做一下比对,在这段代码中registeredKeys参数使用是空数组"[]"。
  仔细阅读u2f-api.js,会发现脚本使用了EXTENSION_ID为"kmendfapggjehodndflmmgagdbamhnfd"的chrome内置扩展完成与U2F设备的通讯。
  在执行3.2节的鉴权过程时,通过跟踪浏览器消息,可以看到第一次向服务器请求后返回的页面中包含如下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的上层函数sign进行了注册。

4. 浏览器兼容测试

  使用购买的U2F设备在PC上对chrome、firefox、IE浏览器进行了测试。其中chrome版本为69,firefox版本为firefox quantum 62, 这两种浏览器目前都支持U2F设备。
  在firefox中使用U2F需要打开一个开关(默认没有打开),如图8所示。

  这里有个有趣的问题,测试时使用的网站仍然是https://demo.yubico.com/u2f ,JS脚本仍然使用的是u2f-api.js,前面提到这个脚本是针对chrome内置扩展应用与USB口通信的,在firefox中怎么也能正常使用呢?
  原因就是firefox实现了自己的u2f对象,而且这个u2f对象的所有属性都是只读的,u2f-api.js没有改写这个对象,从控制台如下输出可以看到:

TypeError: setting getter-only property "u2f"

  IE浏览器使用的版本为11,不支持U2F设备。
  在安卓手机上安装了chrome app,使用U2F设备的BLE模式(蓝牙功能)测试了U2F的支持,没有成功,在chrome调用register函数时,U2F设备没有闪烁。

5. 参考文献

  1. https://fidoalliance.org/how-fido-works/
  2. https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/FIDO-U2F-COMPLETE-v1.2-ps-20
设备 数据 服务 参数 接口 应用 客户 客户端 函数 方法 过程 如图 开发 消息 成功 浏览器 版本 编码 网站 脚本 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技产业 为想互联网科技怎么样 国智互联网科技 互联网 卓越科技 互联网科技股票 亿账通互联网科技 人人互联网科技是培训 小米互联网科技园设置 常德市电业局互联网科技部 南山科技园互联网公司有哪些 注册互联网科技公司经营范围大全 中国互联网十大科技股排名 互联网金融科技展 互联网科技创新的路径 互联网科技大赛获奖感言 世界互联网领先科技成果获奖 互联网企业科技类企业如何做账 富国互联网金融科技混合基金 互联网科技自媒体灵感 高科技和互联网的现代国家 互联网科技园 保险科技 互联网保险 互联网科技工作委员会副主任毛军 剑昆互联网络科技有限公司临桂 湖南诚度互联网科技有限公司 郑州欣桑互联网科技有限公司 神州科技互联网有限公司 云鲸互联网络科技有限公司 科技互联网经济论坛 小牛互联网科技有限公司地址
0