libfido2与WebAuthn跨平台FIDO2认证互兼容问题求助
解决Windows libfido2与Web端WebAuthn跨平台互认兼容问题
问题背景
我用C#开发的Windows应用集成了libfido2发布版DLL,实现FIDO设备的注册与认证,单独运行完全正常;Web端基于WebAuthn实现FIDO2功能,单独运行也没问题。但跨端互认时出现两个核心问题:
- Windows端使用WebAuthn生成的publicKey和CredentialId认证时,报错「FIDO2 operation failed (InvalidSignature)」
- Web端使用libfido2生成的publicKey和CredentialId认证时,抛出「PeterO.Cbor.CBORException Message: Too many bytes」错误
问题根源
这俩问题本质是libfido2和WebAuthn在COSE公钥格式、CBOR编解码、数据序列化细节上的差异:
- InvalidSignature:WebAuthn返回的公钥是完整COSE CBOR对象,Windows端直接转字节传入Verify方法,没有正确提取公钥核心材料;或者ClientData哈希、AuthData的处理逻辑和WebAuthn不匹配。
- Too many bytes:libfido2生成的publicKey可能带有非标准CBOR包装或额外元数据,Web端的PeterO.Cbor解析器无法处理多余字节。
修复方案
一、解决Windows端InvalidSignature错误
- 正确解析WebAuthn的COSE公钥
WebAuthn的publicKey是标准COSE CBOR结构,不能直接转字节传入libfido2的Verify方法,需要先解析CBOR提取公钥的x、y坐标(ES256算法为例),再拼接成libfido2要求的64字节原始格式。 - 统一ClientData哈希计算逻辑
WebAuthn用UTF-8编码的clientDataJSON计算SHA-256哈希,Windows端要确保传入SetClientDataHash的是该哈希值,而非原始字符串。 - 确保AuthData完整性
确认Windows端接收的AuthData是WebAuthn返回的完整原始字节,没有被截断或修改。
二、解决Web端Too many bytes错误
- 规范libfido2公钥存储格式
注册时从FidoCredential获取公钥后,解析CBOR并重新序列化为标准COSE结构,再转Base64存入数据库,避免libfido2的非标准包装干扰Web端解析。 - 统一Base64编码方式
WebAuthn常用Base64URL,libfido2可能用标准Base64,跨端传递时要做格式转换(替换-/_为+/=,补全=符号)。
调整后的代码示例
Windows端认证完成方法(适配WebAuthn公钥)
private void CompleteAuthentication(string rpId, ReadOnlySpan<byte> authData, ReadOnlySpan<byte> signature, FidoExtensions extensions, string publicKey) { // 解析WebAuthn返回的COSE公钥CBOR对象 var coseKey = PeterO.Cbor.CBORObject.DecodeFromBytes(Convert.FromBase64String(publicKey)); // 提取ES256公钥的x、y坐标(算法不同需调整,如RS256取模和指数) byte[] x = coseKey["-2"].GetByteString(); byte[] y = coseKey["-3"].GetByteString(); // 构造libfido2要求的64字节原始公钥格式 byte[] keyBytes = new byte[64]; Buffer.BlockCopy(x, 0, keyBytes, 0, 32); Buffer.BlockCopy(y, 0, keyBytes, 32, 32); using (var assert = new FidoAssertion()) { // 计算ClientData的SHA-256哈希(确保和WebAuthn逻辑一致) byte[] clientDataHash = SHA256.HashData(Encoding.UTF8.GetBytes(Cd)); assert.SetClientDataHash(clientDataHash); assert.Rp = rpId; assert.Count = 1; assert.SetAuthData(authData, 0); assert.SetExtensions(extensions); assert.SetSignature(signature, 0); // 用处理后的公钥验证签名 assert.Verify(0, FidoCose.ES256, keyBytes); } }
Windows端注册时适配Web端的公钥存储逻辑
// 注册成功后获取libfido2生成的公钥 byte[] rawPublicKey = credential.PublicKey; // 解析为标准COSE CBOR对象 var coseKey = PeterO.Cbor.CBORObject.DecodeFromBytes(rawPublicKey); // 重新序列化为标准CBOR字节,转Base64存入数据库 string standardPublicKey = Convert.ToBase64String(coseKey.EncodeToBytes()); // 将standardPublicKey和CredentialId(转Base64URL)存入数据库
Base64与Base64URL转换工具
// 标准Base64转Base64URL(适配WebAuthn) public static string Base64ToBase64Url(string base64) { return base64.Replace('+', '-').Replace('/', '_').TrimEnd('='); } // Base64URL转标准Base64(适配libfido2) public static string Base64UrlToBase64(string base64Url) { base64Url = base64Url.Replace('-', '+').Replace('_', '/'); return base64Url.PadRight(base64Url.Length + (4 - base64Url.Length % 4) % 4, '='); }
内容的提问来源于stack exchange,提问作者Aditya Sharma
相关产品推荐
相关产品推荐

