探索 WebAuthn 在 Passkey 认证中如何使用非对称加密算法和 pubKeyCredParams,以及 credentialPublicKey、CBOR 和 COSE 的作用。
Vincent
Created: August 8, 2025
Updated: August 8, 2025
See the original blog version in English here.
Our mission is to make the Internet a safer place, and the new login standard passkeys provides a superior solution to achieve that. That's why we want to help you understand passkeys and its characteristics better.
2.1 公钥密码学是如何工作的?
2.2 公钥密码学算法的常见类型
4.2 在 pubKeyCredParams 中定义 pubKeyCredParams 数组
5.3 什么是 COSE 密钥格式?
在数字安全领域,Passkey 是新的无密码标准。Passkey 的核心是公钥密码学,它被应用于 Passkey 所基于的 WebAuthn 协议中。在设计或使用 Passkey 认证时,理解公钥密码学在 WebAuthn 中的应用方式至关重要,尤其是在创建、提取、管理和存储 Passkey 方面。公钥存储在信赖方 (RP) 的服务器上,也就是希望通过 Passkey 认证用户的网站后端。而私钥则根据操作系统的不同,安全地存储在硬件安全模块中,例如 Secure Enclave (iOS)、TEE (Android) 或 TPM (Windows)。
在这篇博客中,我们将快速介绍 Passkey 中使用的公钥密码学基础知识。读完本文后,你应该能回答以下问题:
要理解公钥密码学在 WebAuthn 中是如何工作的,我们先来快速了解一下它的一般工作原理以及常见的算法类型。
公钥密码学,也称为非对称加密,与对称加密形成对比,后者使用相同的密钥进行加密和解密。非对称加密使用两个不同的密钥——一个可以公开共享的公钥,以及一个由所有者保密的私钥。
图片来源:https://en.wikipedia.org/wiki/Public-key_cryptography
这种方法不仅能加密数据以保护其机密性,还能对消息进行签名。签名可以验证发送者的身份,并确保消息未被篡改,从而确认其真实性和完整性。
以下是公钥密码学中一些常见算法类型的概述:
类别 | RSA | DSA | ECDSA | EdDSA |
---|---|---|---|---|
发明年份 | 1977 | 1991 | 1999 | 2011 |
算法类型 | 非对称公钥密码学 | 数字签名算法 | 椭圆曲线数字签名 | Edwards 曲线数字签名 |
主要用途 | 安全数据传输 | 签署电子文档 | 安全交易和签名 | 安全交易和签名 |
常见密钥大小 | 1024 至 15360 位 | 1024 至 3072 位 | 160 至 512 位 | 256 位 |
性能 | 因密钥较大而较慢 | 签名速度比 RSA 快 | 使用小密钥计算更快 | 为速度和安全性优化 |
流行度 | 历史上广泛使用 | 不如 RSA 常见 | 日益流行 | 迅速普及 |
移动设备效率 | 在移动设备上效率较低 | 效率中等 | 在移动设备上效率高 | 效率最高 |
密钥存储需求 | 因密钥较大而需求高 | 中等 | 存储空间需求低 | 存储需求极小 |
电池使用 | 消耗较高 | 消耗中等 | 功耗较低 | 对电池保护最优化 |
移动设备适用性 | 因大小和功耗而不太适合 | 适中 | 非常适合 | 非常适合 |
标准采纳情况 | 广泛采纳 (TLS, SSH) | 采纳较少 | 在现代协议中广泛采纳 (TLS, SSH) | 在较新协议中逐渐被采纳 (TLS, SSH) |
对未来威胁的抵抗力 | 易受量子攻击 | 易受量子攻击 | 可能抵抗量子攻击 | 可能抵抗量子攻击 |
通用性 | 跨平台通用性高 | 限于特定用例 | 通用性高 | 通用性高 |
专利状态 | 无专利 | 无专利 | 无专利 | 无专利 |
椭圆曲线密码学 (ECC) 的数学基础,包括 ECDSA 和 EdDSA,涉及椭圆曲线的特性,本文不作探讨。但从上表可以明显看出,其优势推动了它们的普及。我们将在下一节介绍最重要的几点。
椭圆曲线密码学因其较小的密钥尺寸带来的优势,已被移动设备广泛采用:
安全级别 (bits) | RSA 密钥大小 (bits) | ECDSA/EdDSA 密钥大小 (bits) |
---|---|---|
80 | 1024 | 160-223 |
112 | 2048 | 224-255 |
128 | 3072 | 256-383 |
256 | 15360 | 512+ |
在此背景下,安全级别指的是加密系统的强度,具体来说是攻击者破解安全措施的难度级别。它通常以位为单位衡量,表示破解加密或伪造签名所需的工作量(操作次数)。随着安全级别的提高,密钥尺寸的优势变得非常明显,比例最高可达 1:30。
算法 | 密钥大小 | 签名操作 | 签名/秒 |
---|---|---|---|
RSA | 2048 | 0.001012s | 987 |
ECDSA | 256 | 0.000046s | 21565 (x20) |
这些因素使得 ECC 特别适合移动环境,因为在移动环境中,优化存储、处理速度和功耗至关重要。因此,ECC 因其能够在不影响设备性能的情况下提供强大的安全性,在移动计算中越来越受欢迎。尽管如此,至今仍有许多旧版本的广泛协议,如 TLS(用于 HTTPS、FTPS 或 SMTPS)、SSH 或 IPsec,它们仍然支持 RSA,但也开始向支持 ECC 的客户端提供基于 ECC 的变体。
WebAuthn 标准本身不是一个加密标准,而是一个安全协议,它为 Web 应用程序提供基于公钥的强认证,使用户能够使用生物识别、移动设备或硬件安全密钥(例如 YubiKeys)而不是密码登录。因此,WebAuthn 有意对实际使用的底层公钥加密技术保持中立,让我们看看这是如何实现的。
WebAuthn 安全协议促进了用户和**信赖方**之间的加密安全认证。从技术上讲,这意味着信赖方(一个希望与其用户一起使用 Passkey 的网站)需要通过浏览器与用户及其认证器交换密钥,然后认证器将私钥存储在特定的硬件安全模块 (HSM) 中。
对于 Passkey 的注册,有三个重要步骤:
PublicKeyCredentialCreationOptions.pubKeyCredParams
以及其他 PublicKeyCredentialCreationOptions
发出支持的加密算法信号。pubKeyCredParams
列表中它支持的加密算法,并创建一个密钥对以及一个唯一的凭证 ID。它将私钥存储在 HSM 中,然后将公钥和使用的加密算法返回给浏览器。然后,浏览器向信赖方后端发送一个 POST 请求,其中 attestationObject
位于“authData”部分。如果支持的加密算法不匹配,创建过程将失败。attestationObject
。它解析 authData.credentialPublicKey
部分并提取公钥。公钥连同有关所用加密算法和凭证 ID 的信息一起被发送回信赖方。对于后续使用 Passkey 的登录/认证,需要以下步骤(细节简化):
通过观察这两种情况,我们可以看到只有在 Passkey 注册时,公钥和加密算法信息才会在参与者之间传输。
对于后续使用 Passkey 的登录/认证事件,只有挑战和签名是传输数据的一部分。
WebAuthn 标准使用 IANA COSE 算法 ID 来标识所使用的加密算法。**COSE 算法 ID 既用于在 pubKeyCredParams 中发出支持的算法信号,也用于在 credentialPublicKey 中传输实际创建的密钥对类型。**我们将在接下来的两节中重点介绍它们的实现。
IANA 的 COSE 算法列表包含超过 75 种理论上可用于 WebAuthn 标准的算法。大多数负数 ID 的算法是非对称公钥算法,大多数正数 ID 的是对称算法,但这更像是一种惯例,因为也有例外。
正如我们之前指出的,加密算法需要得到认证器和信赖方后端的支持,才能在 WebAuthn 流程中使用。由于大多数信赖方使用现有的 WebAuthn 库,这些库可以访问各种加密算法,因此更重要的是看哪些算法被哪些认证器支持:
名称 | ID | 描述 | Apple | Android | Windows 10 | Windows 11+ | 安全密钥 |
---|---|---|---|---|---|---|---|
RS256 | -257 | 使用 SHA-256 的 RSASSA-PKCS1-v1_5 | ❌ | ❌ | ✅ | ✅ | ✅ |
EdDSA | -8 | EdDSA | ❌ | ❌ | ❌ | ❌ | ✅ (*) |
ES256 | -7 | 带 SHA-256 的 ECDSA (也称为 NIST P-256) | ✅ | ✅ | ❌ | ✅ | ✅ |
(*) = 小部分安全密钥(例如 Yubikeys 5+、Solokey 或 Nitrokey) 摘自 IANA 表格:完整列表请见此处
从这个表中可以看出,要支持所有主流的认证器,RS256 和 ES256 就足够了。社区中有一部分人建议也集成 EdDSA 以进一步提高安全性。另一方面,同样需要存储的凭证 ID 对于 EdDSA 密钥似乎要长得多。截至目前,W3C 关于即将发布的 WebAuthn Level 3 标准的编辑草案建议使用所有这三种算法。
为创建 Passkey 配置支持的加密算法是通过 PublicKeyCredentialCreationOptions
完成的:
const publicKeyCredentialCreationOptions = { challenge: "*", rp: { name: "Corbado", id: "corbado.com", }, user: { id: "user-X", name: "user@corbado.com", displayName: "Corbado Name", }, pubKeyCredParams: [ { alg: -8, type: "public-key", }, { alg: -7, type: "public-key", }, { alg: -257, type: "public-key", }, ], authenticatorSelection: { authenticatorAttachment: "platform", requireResidentKey: true, }, }; const credential = await navigator.credentials.create({ publicKey: publicKeyCredentialCreationOptions, });
这个例子展示了如何在 pubKeyCredParams
中指定算法:通过 alg
指定 ID,并添加 public-key
作为算法类型。在第 29/30 行,PublicKeyCredentialCreationOptions 通过浏览器的 WebAuthn API 传递给认证器。移除对 ES256 或 RS256 的支持将产生错误,强烈不建议这样做。
作为一个工作示例,我们将在 Mac 上的 Passkeys Debugger 中运行以下 PublicKeyCredentialCreationOptions
。
{ "rp": { "id": "www.passkeys-debugger.io", "name": "Relying Party Name" }, "challenge": "AAABeB78HrIemh1jTdJICr_3QG_RMOhp", "pubKeyCredParams": [ { "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -257 } ], "excludeCredentials": [], "timeout": 120000, "authenticatorSelection": { "residentKey": "required", "requireResidentKey": true, "userVerification": "required", "authenticatorAttachment": "platform" }, "hints": [], "attestation": "direct", "user": { "name": "User-2024-08-19", "displayName": "User-2024-08-19", "id": "LlakhOS2vobxxwdkInYP-277Atf0S5OsJax_uBCNNINk" } }
认证器产生的响应被发送到信赖方(作为证明),看起来像这样:
{ "id": "aWMmE4BE9ZzvRKd9rQhdy6ubrlB3COrTRFQANe6ydHg", "rawId": "aWMmE4BE9ZzvRKd9rQhdy6ubrlB3COrTRFQANe6ydHg", "type": "public-key", "response": { "attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIhAIvVNCTlYXX7WKOfeto7WyBQE6uvXpvnNy22kqrMxs_QAiAmanFqalrvD_1fe0Cb2f60ljth4nngckkKJ8JPtqZiO2hhdXRoRGF0YVikt8DGRTBfls-BhOH2QC404lvdhe_t2_NkvM0nQWEEADdFAAAAAK3OAAI1vMYKZIsLJfHwVQMAIGljJhOARPWc70Snfa0IXcurm65Qdwjq00RUADXusnR4pQECAyYgASFYIDP4onRKVHXlhwbmWF4V6jmfsuVuSXchGm6xoceSBGtjIlgg3bxZIbKyE7qPczMZmS0jCGBf9cgajs77EZL-gNAjO0c", "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQUFBQmVCNzhIckllbWgxalRkSklDcl8zUUdfUk1PaHAiLCJvcmlnaW4iOiJodHRwczovL29wb3Rvbm5pZWUuZ2l0aHViLmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ", "transports": ["internal"], "publicKeyAlgorithm": -7 }, "authenticatorAttachment": "platform", "clientExtensionResults": {} }
在下一节中,我们将看到如何从响应中提取公钥和所使用的加密算法。
首先,我们需要研究一下实际的 attestationObject
是如何构建的,因为从上面我们可以看到,它并不是以 JSON 格式传输给信赖方的。
attestationObject 在 WebAuthn 注册过程中扮演着重要角色,它作为认证器提供的证明声明的容器。该对象提供了大量必要信息,供信赖方 (RP) 验证新创建的公钥凭证的来源和完整性。
从核心上讲,attestationObject 是一个复杂的结构。它主要以 CBOR(简明二进制对象表示法)格式编码,然后最终使用 Base64URL 编码。它封装了认证器数据以及一个验证信息真实性的证明声明。因为 Passkey 是用“none”证明创建的,因此不携带证明声明,所以我们本文不讨论这一点。顺便提一下:我们说主要是 CBOR,因为由于可选扩展需要可变长度,还涉及到一些非标准的 CBOR 前缀。我们不会深入探讨这一点,关于其复杂性的讨论可以在这里找到。
摘自 WebAuthn 规范
在认证器数据 (authData) 中,新生成的公钥与用户的凭证 ID 一起被安全存储,并可由信赖方检索。由于开发者在任何基于 WebAuthn 的系统中都必须处理从 attestationObject 中提取公钥的任务,因此理解其架构非常重要。
CBOR(简明二进制对象表示法)是一种数据格式,旨在以紧凑、高效和可扩展的方式编码数据。它是二进制的,这使得它比其基于文本的范本 JSON 更小、解析更快。以下示例说明了 JSON 和 CBOR 的不同之处(文本 vs. 二进制):
JSON 文本 | CBOR 二进制数据 | CBOR 解码后 |
---|---|---|
Name:Corbado | A1 64 4E 61 6D 65 67 43 6F 72 62 61 64 6F | A1 # map(1) 有一个条目 64 # text(4) 4E616D65 # Name; 67 # text(7) 436F726261646F # Corbado |
粗体是 Name。 斜体是 Corbado。(更多信息请见 https://cbor.io/)
在 WebAuthn 的背景下,使用 CBOR 有几个原因:
使用 CBOR 特别适合编码公钥和 attestationObject,因为它需要处理我们上面讨论的不同格式和长度。例如,一个 RSA 密钥与一个 ECDSA 密钥相比,会有不同的属性和大小。在 attestationObject 中,公钥以 COSE 密钥格式存储,该格式基于 CBOR。
COSE 密钥结构建立在 CBOR 映射对象之上。它定义了一种结构化的密钥表示方式,涵盖了各种密钥类型及其相应的参数,例如 RSA 模数和指数,或椭圆曲线公钥坐标。这种标准化格式允许密钥以一致的方式序列化和反序列化,无论其底层加密算法如何,此外还有我们上面讨论过的加密算法 ID。
通过利用 CBOR 和 COSE 密钥格式,WebAuthn 可以促进广泛的加密操作,同时确保有效载荷尽可能小,并保持对未来加密技术更新的适应性。这一选择符合 WebAuthn 提供安全、高效和前向兼容的 Web 认证协议的目标。
在 WebAuthn 中解码 attestationObject 时,开发者面临一个重要抉择:是开发自定义解决方案,还是利用成熟的库。这个过程的复杂性和关键性不容小觑。手动实现 Base64URL 和 CBOR 解码虽然技术上可行,但会引入可能损害认证过程完整性的细微错误风险。
为了确保签名的加密验证以及所有其他验证步骤的准确性,强烈建议使用经过充分测试且被广泛采用的 WebAuthn 库。这些库是基于加密专业知识构建的,能够处理解码和验证 attestationObject 的细节,包括:
通过依赖信誉良好的库,开发者不仅节省了时间和资源,还获得了安心。公钥一旦通过这些可靠的工具提取和验证,就可以安全地存储在数据库中,准备用于安全的认证会话。这种方法确保了对协议规范的遵守,并维持了 WebAuthn 实现中预期的安全态势。为简单起见,我们将使用 SimpleWebauthn Debugger,它返回一个完全解码的版本,其中 CBOR 字段被转换为展开的 JSON:
{ "id": "aWMmE4BE9ZzvRKd9rQhdy6ubrlB3COrTRFQANe6ydHg", "rawId": "aWMmE4BE9ZzvRKd9rQhdy6ubrlB3COrTRFQANe6ydHg", "type": "public-key", "response": { "attestationObject": { "fmt": "packed", "attStmt": { "alg": "ES256 (-7)", "sig": "MEUCIQCL1TQk5WF1-1ijn3raO1sgUBOrr16b5zcttpKqzMbP0AIgJmpxampa7w_9X3tAm9n-tJY7YeJ54HJJCifCT7amYjs" }, "authData": { "rpIdHash": "t8DGRTBfls-BhOH2QC404lvdhe_t2_NkvM0nQWEEADc", "flags": { "userPresent": true, "userVerified": true, "backupEligible": false, "backupStatus": false, "attestedData": true, "extensionData": false }, "counter": 0, "aaguid": "adce0002-35bc-c60a-648b-0b25f1f05503", "credentialID": "aWMmE4BE9ZzvRKd9rQhdy6ubrlB3COrTRFQANe6ydHg", "credentialPublicKey": "pQECAyYgASFYIDP4onRKVHXlhwbmWF4V6jmfsuVuSXchGm6xoceSBGtjIlgg3bxZIbKyE7qPczMZmS0jCGBf9cgajs77EZL-gNAjO0c", "parsedCredentialPublicKey": { "keyType": "EC2 (2)", "algorithm": "ES256 (-7)", "curve": 1, "x": "M_iidEpUdeWHBuZYXhXqOZ-y5W5JdyEabrGhx5IEa2M", "y": "3bxZIbKyE7qPczMZmS0jCGBf9cgajs77EZL-gNAjO0c" } } }, "clientDataJSON": { "type": "webauthn.create", "challenge": "AAABeB78HrIemh1jTdJICr_3QG_RMOhp", "origin": "https://opotonniee.github.io", "crossOrigin": false }, "transports": ["internal"], "publicKeyAlgorithm": -7 }, "authenticatorAttachment": "platform", "clientExtensionResults": {} }
SimpleWebAuthn 库用于解码完整的 attestationObject。如我们所见,所有信息现在都可读了。所有加密信息都是 authData 部分中 credentialPublicKey 的一部分,库已将其展开为 parsedCredentialPublicKey 语句。在规范中,有几个关于 COSE 密钥格式外观的示例。
{ "parsedCredentialPublicKey": { "keyType": "EC2 (2)", "algorithm": "ES256 (-7)", "curve": 1, "x": "M_iidEpUdeWHBuZYXhXqOZ-y5W5JdyEabrGhx5IEa2M", "y": "3bxZIbKyE7qPczMZmS0jCGBf9cgajs77EZL-gNAjO0c" } }
此输出以人类可读的形式整齐地解析并展示了 credentialPublicKey 的所有加密元素。这个特定实例揭示了一个用于 ES256 算法的 EC2 密钥,如算法参数以及 x 和 y 坐标的存在所示。
相比之下,Windows 10 系统的输出如下:
{ "parsedCredentialPublicKey": { "keyType": "RSA (3)", "algorithm": "RS256 (-257)", "modulus": "mzRVwAL6jbccWT4NQ3rQWEYLkTKkEBeHPPUn0CXT8VwvvGE_IaXDeP9ZzcA7WoX3z6E0l_L-XZmRuKc9cO7BkiYyz3jOg_pNFTz5Ap9a1f_9H0m4mpL-30WHQZi1skB5f6zt8sO8q7rBYH0mRmH8LdCrhJRhVjB_UxbcAbYlpV98M5g-5OBs_boNXtMhMoyp-IOeGChp07wwSLVOH3hKMoxlU27hZ3QvK2LRWosNKhXSHcU9IOC0XOyhlZ5rtPX2ae3KsSE1H2rEJVcMaVMRAg8yx2SRM98pDvf829smrnJPdMBojKftne2j8o84i_xyDJ_jARlyVj0kxR37u0AVQQ", "exponent": 65537 } }
在这里,算法 ID 是 -257,对应于 RS256,我们可以看出表征此公钥的属性与 ECDSA 密钥的属性有显著不同。COSE 密钥格式允许为每种类型指定唯一的属性:
属性/类型 | ECDSA | RSA |
---|---|---|
密钥类型 | EC2 (椭圆曲线 2) | RSA (3) |
算法 | ES256 (-7) | RS256 (-257) |
唯一属性 | 曲线 X 坐标 Y 坐标 | 模数 指数 |
此外,RSA 模数比 ES256 密钥中使用的椭圆曲线坐标长得多,这强调了我们之前关于密钥大小差异和不同加密算法固有要求的讨论。
在了解了公钥密码学的基础知识并理解了它如何被整合到 WebAuthn / FIDO2 标准中之后,我们对信赖方有两个主要建议:
关键是不要重复造轮子: 利用成熟库中蕴含的集体知识。自定义实现可能会引入错误和安全漏洞,从而破坏认证的有效性和系统的整体安全性。
在这篇博客文章中,我们探讨了公钥密码学的基础知识,以回答最初的三个核心问题:
此外,我们还强调了 WebAuthn 协议独立于特定加密算法的特性,这显著增强了其灵活性和对新兴加密方法的未来适应性。我们希望本文也能帮助其他开发者在调试和理解与 WebAuthn 中公钥密码学相关的概念/问题时有所帮助。
Enjoyed this read?
🤝 Join our Passkeys Community
Share passkeys implementation tips and get support to free the world from passwords.
🚀 Subscribe to Substack
Get the latest news, strategies, and insights about passkeys sent straight to your inbox.
Related Articles
Table of Contents