WebAuthn中Yubico密钥userHandle返回异常问题问询
Yubico密钥无密码登录:解决AGGUID无法获取及userHandle异常问题
问题背景
在用Yubico密钥实现无密码登录时,需在登录阶段解密数据,遇到两个核心问题:
- 原本计划使用密钥AGGUID,但该值仅在注册(
navigator.credentials.create())阶段可获取,登录(navigator.credentials.get())阶段无法拿到,此方案不可行。 - 尝试通过
create()的userHandle参数存储数据,登录时读取,但无论是否传入该参数,get()返回的userHandle始终是ArrayBuffer(1)。
问题原因
1. AGGUID的规范限制
WebAuthn规范明确规定,AGGUID仅在注册流程的attestation响应中返回,断言(登录)流程不会提供该字段,因此无法通过此途径获取密钥标识用于解密。
2. userHandle异常的本质
- WebAuthn的
PublicKeyCredentialCreationOptions参数中不存在userHandle字段,你传入的该参数会被浏览器忽略,根本不会存储到密钥上。 - 你看到的
ArrayBuffer(1),实际是你在create()的user.id中传入的Uint8Array(1)——user.id才是WebAuthn规范中定义的、会被密钥关联存储的用户标识,登录时会原样返回。
解决方案
正确存储自定义数据
将解密所需的编码后数据(比如用户唯一标识、加密密钥片段等)作为user.id传入注册流程,替代无效的userHandle参数:
// 注册阶段的create()参数(修正版) const createOptions = { attestation: "direct", authenticatorSelection: { authenticatorAttachment: 'cross-platform', userVerification: 'required', requireResidentKey: true, residentKey: 'required' }, challenge: new Uint8Array([113, 73, 120, 104, 50, 115, 117, 82, 57, 109, 111, 81, 119, 85, 65, 120, 69, 105, 108, 114, 112, 103, 53, 101, 68, 65, 73, 89, 85, 67, 71, 67]), pubKeyCredParams: [{ alg: -7 }], // 根据实际加密算法调整,-7为ES256 rp: { id: 'domain', name: 'localhost' }, timeout: 10000, user: { id: new Uint8Array([/* 此处放入你需要存储的32字节数据,比如用户ID的哈希值 */]), name: 'hello@netrizon.eu', displayName: 'Świerżewski' } }; const credential = await navigator.credentials.create({ publicKey: createOptions });
登录阶段读取数据
登录时,response.userHandle会返回注册时传入的user.id,将其转换为对应格式即可获取存储的数据:
// 登录阶段的get()参数 const getOptions = { allowCredentials: [{/* 填入已注册的凭证信息,包括id、type、transports */}], challenge: new Uint8Array([68, 78, 65, 120, 97, 80, 56, 50, 78, 117, 71, 89, 86, 108, 86, 117, 65, 111, 114, 121, 78, 97, 105, 98, 81, 80, 104, 82, 101, 74, 86, 82]), rpId: "domain", timeout: 60000, userVerification: "required" }; const assertion = await navigator.credentials.get({ publicKey: getOptions }); // 读取并转换userHandle const storedData = new Uint8Array(assertion.response.userHandle); // 用storedData进行解密操作
注意事项
- Yubico密钥对
user.id的长度有上限(通常建议不超过64字节),需确保存储的数据在限制范围内。 - 存储的数据需提前编码为Uint8Array,避免字符编码问题。
内容的提问来源于stack exchange,提问作者Swiru
相关产品推荐
相关产品推荐

