news 2026/9/28 2:43:15

jose 中 AKP 类型 JWK(JWK_AKP_Public)详解:ML-DSA 后量子签名密钥的表示、生成与导入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
jose 中 AKP 类型 JWK(JWK_AKP_Public)详解:ML-DSA 后量子签名密钥的表示、生成与导入
  • 网络安全
  • 认证鉴权
  • 后端

【免费下载链接】jose

JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes

项目地址:https://gitcode.com/gh_mirrors/jo/jose
点击查看免费下载

本文以 jose 仓库中 docs/types/interfaces/JWK_AKP_Public.md 文档为主体,系统讲解 jose 对 AKP 类型 JSON Web Key(JWK)的类型建模:JWK_AKP_Public接口的每个属性含义、与 ML-DSA 后量子签名算法的绑定关系,以及密钥生成、导出、导入、指纹计算等完整实战流程。读完本文,你将能正确构造、校验和运用 AKP 公钥 JWK,在支持 WebCrypto 的运行时上完成 ML-DSA 签名的 JWS 场景落地。

一、AKP 是什么:后量子签名密钥在 JWK 中的表示

JWK(RFC 7517)通过kty(Key Type)成员区分密钥类型。jose 支持'EC' | 'RSA' | 'OKP' | 'AKP' | 'oct'五种类型(见 src/types.d.ts)。其中AKP是面向ML-DSA(Module-Lattice-Based Digital Signature Algorithm,NIST FIPS 204 定义的后量子数字签名算法)密钥新增的类型,用于承载 ML-DSA-44、ML-DSA-65、ML-DSA-87 三种参数集的公钥与私钥。

从源码结构看,AKP 与 ML-DSA 的绑定非常直接:在 src/lib/jws_algorithms.ts 中,mldsa(bits)函数将'ML-DSA-44'、'ML-DSA-65'、'ML-DSA-87'三个 JWS 算法标识统一映射到kty: ['AKP'],并以算法标识本身作为 WebCrypto 算法名(源码注释明确写道:“ML-DSA names its WebCrypto algorithm and its Node key type after the JWA identifier”)。也就是说,一个kty为AKP的 JWK,其alg只会是这三个 ML-DSA 标识之一。

JWK_AKP_Public就是 jose 为这种公钥 JWK 提供的 TypeScript 便利接口,其定义位于 src/types.d.ts:

/** Convenience interface for Public AKP JSON Web Keys */ export interface JWK_AKP_Public extends JWKParameters { /** JWK "alg" (Algorithm) Parameter */ alg: string /** AKP JWK "pub" (The Public key) Parameter */ pub: string }

二、JWK_AKP_Public 接口属性总览

JWK_AKP_Public继承JWKParameters(通用 JWK 参数集,见 src/types.d.ts),自身新增两个必选成员。原文档所列全部属性如下:

属性类型必选说明
algstring是JWK "alg"(Algorithm)参数,AKP 键必须显式声明算法标识
pubstring是AKP JWK "pub"(The Public key)参数,Base64URL 编码的公钥
ext?boolean否JWK "ext"(Extractable)参数,标记密钥是否可导出
key_ops?string[]否JWK "key_ops"(Key Operations)参数,如['verify']
kid?string否JWK "kid"(Key ID)参数,用于密钥标识
kty?string否JWK "kty"(Key Type)参数,AKP 键建议显式填写'AKP'
use?string否JWK "use"(Public Key Use)参数,如'sig'
x5c?string[]否JWK "x5c"(X.509 Certificate Chain)参数
x5t?string否JWK "x5t"(X.509 Certificate SHA-1 Thumbprint)参数
x5t#S256?string否JWK "x5t#S256"(X.509 Certificate SHA-256 Thumbprint)参数
x5u?string否JWK "x5u"(X.509 URL)参数

一个典型的 AKP 公钥 JWK 对象形如:

{ "kty": "AKP", "alg": "ML-DSA-65", "pub": "AhGV...(Base64URL 编码的公钥)", "kid": "ml-dsa-65-key-01", "use": "sig", "key_ops": ["verify"] }

三、必选成员:alg 与 pub

与 RSA/EC/OKP 通过n/e、crv/x/y等成员承载密钥材料不同,AKP 键只有两个必选成员,语义差异值得注意。

alg(Algorithm):AKP 键必须携带alg。这一点在导入环节被强制校验:在 src/key/import.ts 的importJWK中,当kty === 'AKP'时,若 JWK 上缺失或为空字符串alg,会直接抛出TypeError('missing "alg" (Algorithm) Parameter value')。这与 RSA/EC/OKP 不同——后者允许在调用importJWK时通过第二个参数补传算法标识,而 AKP 分支不仅要求alg存在,还要求调用方传入的alg参数必须与 JWK 上的alg完全一致,否则抛出TypeError('JWK alg and alg option value mismatch')。原因在于 ML-DSA 的 WebCrypto 算法名就是 JWA 标识本身(ML-DSA-44/65/87),JWK 中的alg直接决定了底层subtle.importKey使用的算法,因此不允许被外部覆盖。

pub(The Public key):承载 Base64URL 编码的 ML-DSA 公钥本体。在 src/lib/jwk_to_key.ts 的jwkToKey中,AKP 键的alg会被保留在传入crypto.subtle.importKey('jwk', ...)的keyData里(if (keyData.kty !== 'AKP') { delete keyData.alg }),其余类型的alg一律删除,只有 AKP 例外——因为 WebCrypto 需要借助该alg成员确定 ML-DSA 的算法与参数集。

四、继承自 JWKParameters 的可选通用参数

除alg/pub外,其余属性均继承自JWKParameters(见 src/types.d.ts),与 EC/OKP/RSA 公钥 JWK 共用同一套通用成员语义:

  • kid:Key ID,用于在多密钥(如 JWKS)场景中标识具体密钥,配合 JWS 头部的kid完成密钥匹配。
  • use:建议取值'sig'或'enc'。ML-DSA 属于签名算法,AKP 公钥一般标记为'sig'。在导入为 WebCrypto 密钥时该成员会被删除(见 src/lib/jwk_to_key.ts),不参与算法选择。
  • key_ops:密钥允许的操作列表。JWS 验签场景下公钥为['verify']。jwkToKey会将其作为importKey的keyUsages(若未提供则回退到算法描述符的默认 usages)。
  • ext:Extractable 布尔值,决定密钥是否可被exportKey导出。jwkToKey中默认值为!isPrivate(公钥默认可导出、私钥默认不可导出)。
  • x5c/x5t/x5t#S256/x5u:X.509 证书链、SHA-1/SHA-256 指纹与证书 URL,用于把密钥绑定到 PKI 证书体系。AKP 键在 jose 中没有独立的 X.509 导入路径,这些成员仅在 JWK 层面透传,实践中 ML-DSA 密钥通常不携带它们。

五、kty 参数与 AnyJWK 判别联合

值得强调的是,JWK_AKP_Public中的kty是可选的(类型为string),接口本身并不把kty锁死为'AKP'。这是所有JWK_*_Public/Private便利接口的共同设计:它们只描述“某个类型的键需要哪些成员”,便于复用JWKParameters的通用成员。

若需要强制kty并借助 TypeScript 判别联合收窄类型,应使用AnyJWK:

export type AnyJWK = | (JWK_EC_Private & { kty: 'EC' }) | (JWK_EC_Public & { kty: 'EC' }) | (JWK_RSA_Private & { kty: 'RSA' }) | (JWK_RSA_Public & { kty: 'RSA' }) | (JWK_OKP_Private & { kty: 'OKP' }) | (JWK_OKP_Public & { kty: 'OKP' }) | (JWK_AKP_Private & { kty: 'AKP' }) | (JWK_AKP_Public & { kty: 'AKP' }) | (JWK_oct & { kty: 'oct' })

以上定义见 src/types.d.ts。在AnyJWK中,JWK_AKP_Public与{ kty: 'AKP' }相交,从而可以在业务代码中通过if (jwk.kty === 'AKP')收窄到 AKP 形状,安全地读取pub、alg。对应文档见 docs/types/type-aliases/AnyJWK.md。

六、JWK_AKP_Private:从私钥到公钥的完整形状

AKP 私钥由JWK_AKP_Private表示,它在公钥接口基础上新增一个必选成员priv(见 src/types.d.ts):

export interface JWK_AKP_Private extends JWK_AKP_Public { /** AKP JWK "priv" (The Private Key) Parameter */ priv: string }

即私钥 JWK 至少包含kty: 'AKP'、alg、pub、priv四个成员,其中priv为 Base64URL 编码的私钥种子。在 src/lib/jwk_to_key.ts 中,jose 通过isPrivate = !!(jwk.d || jwk.priv)判定键是否为私钥,AKP 正是通过priv参与判定。对应接口文档见 docs/types/interfaces/JWK_AKP_Private.md。

七、实战:AKP 密钥对的生成、导出与导入

jose 对 AKP 键的完整支持链路贯穿密钥生命周期,均可在 Node.js、浏览器、Cloudflare Workers、Deno、Bun 等支持 WebCrypto 的运行时上使用(ML-DSA 的具体可用性以运行时为准)。

1. 生成 ML-DSA 密钥对

generateKeyPair支持'ML-DSA-44' | 'ML-DSA-65' | 'ML-DSA-87'(见 src/key/generate_key_pair.ts)。由于 ML-DSA 没有曲线概念,crv选项对它无效——测试注释也明确写道:“RSA and ML-DSA have no curve, so nothing is being substituted and the option stays inert”(见 test/jwk/generate_key_pair.test.ts):

import { generateKeyPair, exportJWK } from 'jose' // 私钥默认不可导出;如需导出 JWK 需显式打开 extractable const { publicKey, privateKey } = await generateKeyPair('ML-DSA-65', { extractable: true, }) console.log(publicKey) // CryptoKey { type: 'public', algorithm: { name: 'ML-DSA-65' }, extractable: true, usages: ['verify'] }

2. 导出为 AKP 公钥 JWK

const publicJwk = await exportJWK(publicKey) console.log(publicJwk) // { kty: 'AKP', alg: 'ML-DSA-65', pub: 'AhGV...' }

导出过程有一个值得注意的细节:WebCrypto 的subtle.exportKey('jwk', ...)本身不会返回alg,而 jose 在 src/key/export.ts 中会先剔除ext、key_ops、alg、use,再对 AKP 键把alg重新附加回去(if (jwk.kty === 'AKP') { jwk.alg = alg })。这是因为 AKP 键的alg是算法标识本身,缺失会破坏 JWK 的自描述性,也与导入时的强校验形成闭环。注意导出要求密钥extractable === true,否则抛出TypeError('non-extractable CryptoKey cannot be exported as a JWK')。

3. 导入 AKP JWK

import { importJWK } from 'jose' const publicKey = await importJWK(publicJwk) // 等价写法:算法取自 JWK.alg // const publicKey = await importJWK(publicJwk, 'ML-DSA-65') // 错误用法:与 JWK.alg 不一致会抛错 // const wrong = await importJWK(publicJwk, 'ML-DSA-44') // TypeError: JWK alg and alg option value mismatch

如第三节所述,AKP 分支要求alg必须存在于 JWK 且与调用参数一致(见 src/key/import.ts),底层的jwkToKey再把保留alg的 JWK 交给crypto.subtle.importKey('jwk', ...)(见 src/lib/jwk_to_key.ts)。

4. 用于 JWS 验签

导入后的公钥可直接参与compactVerify、flattenedVerify、generalVerify等 JWS 验签流程,例如配合jwtVerify校验携带 ML-DSA 签名的 JWT(见 docs/jws/compact/verify/functions/compactVerify.md、docs/jwt/verify/functions/jwtVerify.md):

import { jwtVerify } from 'jose' const { payload, protectedHeader } = await jwtVerify(jwt, publicKey) // protectedHeader.alg === 'ML-DSA-65'

八、AKP JWK 指纹计算与本地 JWK 集匹配

指纹(Thumbprint):calculateJwkThumbprint对 AKP 键使用固定的成员子集{ alg, kty, pub }计算 RFC 7638 指纹——即alg和pub都是参与哈希的必选成员(见 src/jwk/thumbprint.ts):

import { calculateJwkThumbprint, calculateJwkThumbprintUri } from 'jose' const thumbprint = await calculateJwkThumbprint({ kty: 'AKP', alg: 'ML-DSA-44', pub: '...', }) const uri = await calculateJwkThumbprintUri({ kty: 'AKP', alg: 'ML-DSA-44', pub: '...', })

测试 test/jwk/thumbprint.test.ts 验证了该行为:缺alg抛ERR_JWK_INVALID("alg" (Algorithm) Parameter missing or invalid),缺pub抛ERR_JWK_INVALID("pub" (Public key) Parameter missing or invalid)。指纹算法同样支持'sha256' | 'sha384' | 'sha512',默认sha256。对应文档见 docs/jwk/thumbprint/functions/calculateJwkThumbprint.md。

本地 JWK 集(createLocalJWKSet):在 src/jwks/local.ts 的键匹配逻辑中有一个针对 AKP 的特殊规则——(jwkAlg === undefined ? kty !== 'AKP' : alg === jwkAlg):当 JWKS 中的某个键没有声明alg时,AKP 键会被排除在匹配候选之外。这再次印证了“AKP 键必须携带alg才能参与运算”的约束。对应文档见 docs/jwks/local/functions/createLocalJWKSet.md。

九、使用注意事项与运行时前提

综合源码实现,使用 AKP 类型 JWK 时有以下关键约束:

  1. alg不可缺失、不可覆盖:构造 AKP JWK 时alg与pub都是必填;导入时外部传入的算法标识必须与jwk.alg一致(src/key/import.ts)。
  2. alg只能取 ML-DSA 标识:当前 jose 将 AKP 键的算法限定为ML-DSA-44、ML-DSA-65、ML-DSA-87(src/lib/jws_algorithms.ts),JWS 算法与 WebCrypto 算法同名。
  3. 运行时支持是前提:类型注释明确说明 JWS 算法标识的可用性“additionally depends on the runtime”(见 src/types.d.ts)。在旧版本运行时上使用 ML-DSA 可能抛出JOSENotSupported,生产环境应先在目标运行时验证crypto.subtle是否支持对应算法。
  4. 私钥默认不可导出:generateKeyPair生成的私钥extractable默认为false,需要导出私钥 JWK 时必须显式传入{ extractable: true }(见 docs/key/generate_key_pair/functions/generateKeyPair.md)。
  5. 导出结果自动回填alg:exportJWK产出的 AKP JWK 会重新附加alg成员,因此从 jose 导出的 AKP JWK 一定自包含算法标识(src/key/export.ts)。

十、小结

JWK_AKP_Public是 jose 为后量子签名密钥提供的第一公民支持:它定义了 AKP 公钥 JWK 的完整 TypeScript 形状,必选成员alg(ML-DSA 算法标识)与pub(Base64URL 公钥)贯穿生成、导出、导入、指纹计算与 JWKS 匹配的全链路。对开发者而言,只要记住“AKP 键必须自带alg、算法不可覆盖、可用性取决于运行时”这三点,就能把 ML-DSA 后量子签名平滑接入现有的 JWS/JWT 体系。相关文档与实现入口:接口定义见 docs/types/interfaces/JWK_AKP_Public.md 与 src/types.d.ts,算法绑定见 src/lib/jws_algorithms.ts,密钥生命周期见 src/key/import.ts、src/key/export.ts、src/key/generate_key_pair.ts。

  • 网络安全
  • 认证鉴权
  • 后端

【免费下载链接】jose

JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes

项目地址:https://gitcode.com/gh_mirrors/jo/jose
点击查看免费下载
上一篇:AnimatedTextInput在Jobandtalent应用中的实战应用案例分析:如何打造极致用户体验的iOS输入组件
下一篇:go-clean-arch Kubernetes部署:Helm Chart编写与集群配置

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 2:43:09

pixi auth 完全指南:为私有频道与上传服务配置登录凭证

开发工具CLI包管理器任务调度 【免费下载链接】pixi Powerful system-level package manager for Linux, macOS and Windows written in Rust – building on top of the Conda ecosystem. 项目地址: https://gitcode.com/gh_mirrors/pi/pixi 点击查看 免费下载 导…

作者头像 李华
网站建设 2026/9/28 2:40:10

树莓派+Pixhawk:无人机自主巡航与视觉精准降落实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华