| English Version | 中文版 |
RFC 编号:NPS-RFC-0002 标题:NID 证书改用 X.509 + ACME 状态:Accepted 作者:Ori Lynn iamzerolin@gmail.com(LabAcacia) Shepherd:待定——PR 开立时指派 创建日期:2026-04-21 最后更新:2026-05-27 接受日期:2026-05-27 激活日期:(首个参考 SDK 发版时填写) 取代:无 被取代于:无 影响的规范:NPS-3 NIP、tools/nip-ca-server(所有语言版本)、spec/error-codes.md 影响的 SDK:.NET、Python、TypeScript、Java、Rust、Go —
把 NIP 自研证书格式替换为 X.509v3 证书,通过 LabAcacia 注册的
Extended Key Usage (EKU) OID 标记 “agent-identity”;把 NIP CA 的签发
接口从自研 REST API 迁到 ACME (RFC 8555),新增 agent-01 挑战
类型以适配 NID 私钥可控的场景。主签名算法仍为 Ed25519(RFC 8410 —
Ed25519 in X.509);CRL / OCSP 吊销语义保持不变。
本 RFC 回应 2026-04-20 的评审意见:“CA 不如直接兼容现有的 CA 套路”。
评论是对的。当前 NIP CA(tools/nip-ca-server-*)在操作模型上已经
复刻了经典 X.509 CA 的流程——CSR → 签发 → CRL/OCSP 吊销 → 续签——
但用的是自研序列化。这带来两项不必要的成本:
step-ca、
HashiCorp Vault PKI、HSM 厂商、Kubernetes 的 cert-manager——
没一个能直接签发、验证或托管 NIP 证书。6 种语言的 NIP CA Server
每一家都在重复实现同一套仪式。改到 X.509 + ACME 的代价是暴露一个 ASN.1 解析面、证书体积 2–4 倍, 换来的是 15 年的 PKI 工具链复用;以及未来可以跑一个 “Let’s Encrypt for Agents” 的公共服务,或者把 NID 签发通过交叉签名委托给现有 CA。
IdentFrame 的 wire 格式——证书仍以 cert_chain 字节
承载,只是内部编码从 NIP 自研改为 X.509。NID 格式(nid:{algo}:{base64url(pubkey)})。IdentFrame.cert_chain 编码 从 nip-cert-v1(自研)改为
DER 编码的 X.509 证书链,按下列规则拼接或长度前缀。
Subject 字段映射:
| X.509 字段 | NIP 值 |
|---|---|
Subject CN |
NID 字符串(nid:ed25519:...) |
| Subject Alternative Name (SAN) URI | 同一个 NID 以 URI 形式再写一遍(RFC 5280 合规) |
| Issuer | 签发 CA 的 NID |
| NotBefore / NotAfter | 标准 X.509 有效期 |
| Public Key | Ed25519(RFC 8410 OID 1.3.101.112) |
| Serial Number | 128-bit 随机(按 CA/B Forum baseline) |
关键扩展——Extended Key Usage:
申请 LabAcacia IANA Private Enterprise Number (PEN)——拿到后,
保留 OID arc 1.3.6.1.4.1.<PEN>.1。
| OID | 含义 |
|---|---|
1.3.6.1.4.1.<PEN>.1.1 |
agent-identity —— 主体是一个 NPS Agent |
1.3.6.1.4.1.<PEN>.1.2 |
node-identity —— 主体是一个 NPS Node |
1.3.6.1.4.1.<PEN>.1.3 |
ca-intermediate-agent —— 本 CA 可签发 agent-identity 证书 |
EKU 标为 critical。不认这些 EKU 的 verifier 在 NIP 语义下 MUST 拒绝该证书。(通用 TLS 客户端也会拒绝——这是 故意的, NID 证书不能被当成 TLS server 证书误用。)
自定义非 critical 扩展——nid:assurance-level:
为 NPS-RFC-0003 预留。本 RFC 只定义编码形状:
nid-assurance-level EXTENSION ::= {
SYNTAX NidAssuranceLevel
IDENTIFIED BY id-nid-assurance-level -- 1.3.6.1.4.1.<PEN>.2.1
}
NidAssuranceLevel ::= ENUMERATED {
anonymous (0),
attested (1),
verified (2)
}
非 critical,使 v0.1 verifier 可以直接忽略;RFC-0003 在真正启用 assurance-level 强制时再翻成 critical。
无。X.509 证书链仍在 IdentFrame 里传,NWM 透明。
spec/error-codes.md 新增:
| 错误码 | NPS 状态码 | 说明 |
|---|---|---|
NIP-CERT-FORMAT-INVALID |
NPS-CLIENT-BAD-FRAME |
证书链不是 DER 编码 X.509,或 ASN.1 解析失败 |
NIP-CERT-EKU-MISSING |
NPS-CLIENT-BAD-FRAME |
必需的 NPS EKU(agent-identity 或 node-identity)缺失 |
NIP-CERT-SUBJECT-NID-MISMATCH |
NPS-CLIENT-BAD-FRAME |
证书 Subject CN / SAN URI 与 IdentFrame 里的 NID 不一致 |
NIP-ACME-CHALLENGE-FAILED |
NPS-CLIENT-BAD-FRAME |
ACME agent-01 挑战校验失败 |
ACME agent-01 挑战——专为 NID 身份校验设计的新挑战类型,
对齐 RFC 8555 §8 的 HTTP / DNS / TLS-ALPN 挑战:
Client (Agent) ACME Server (NIP CA)
│ │
│── newAccount (Ed25519 JWK) ──→ │
│ ←── 201 + kid ──────────────── │
│ │
│── newOrder (identifiers) ────→ │
│ identifier: type=nid, │
│ value=nid:ed25519:... │
│ ←── order + authz URL ──────── │
│ │
│── GET authz URL ──────────────→ │
│ ←── challenge: type=agent-01, │
│ token=T ────────────────── │
│ │
│ (Agent 用 NID 私钥对 `T` │
│ 签名;可以在已公告的 │
│ endpoint 上 /nip/auth/T │
│ 公开 signature,也可以 │
│ 作为 JWS 直接 POST 回 │
│ ACME 服务端) │
│ │
│── POST challenge (signed T) ─→ │
│ ←── 200 + status=valid ──────── │
│ │
│── finalize (CSR) ─────────────→ │
│ ←── 200 + cert URL ──────────── │
│── GET cert URL ───────────────→ │
│ ←── 200 + X.509 DER ────────── │
agent-01 挑战以 TLS-ALPN-01 的简洁度证明 NID 私钥持有:单个
签名 token,不依赖外部 DNS / HTTP。服务端 MUST 使用常数时间(timing-safe)
比较验证签名后的挑战 token,以防止针对 NID 私钥的时序预言攻击。
(NIP §10.2 涉及的是另一个问题——OCSP 响应时间规范化,与此处无关。)
cert_format 字段
(v1-proprietary | v2-x509)。跑 v0.2 栈的部署可以分阶段发布。min_agent_version 抬高,按 RFC 流程走 21 天窗口。保留现有证书格式;提供一个单向 X.509 导出以做互通(例如
nipc export --x509)。
用 X.509 但继续 /certs/issue 这类自研 REST 接口。
step-ca、boulder、pebble),再逐步移植。System.Security.Cryptography.X509Certificates、
Python 用 cryptography、Rust 用 x509-parser 等)——禁止
手搓。cert_format 版本字段缓解。newAccount 给账号持有人
轮换密钥的能力。NIP CA MUST 强制账号密钥算法为 Ed25519
(对齐 NID 算法族)。| 阶段 | 范围 | 出口条件 |
|---|---|---|
| 1 | .NET NIP + .NET CA Server 能发/收 X.509;.NET CA 实现 ACME agent-01;v1 + v2 并存 |
单测绿;跨格式互通(v1 client ↔ v2 server 双向) |
| 2 | 6 SDK + 6 CA Server 都支持 X.509 + ACME;cert_format=v2 默认 off |
跨 SDK 证书接收矩阵全绿;6 种 CA 都能跑 ACME 签发 |
| 3 | cert_format=v2 默认 on;21 天 v1 废弃通告 |
1 个发布周期无回归 |
| 4 | 移除 v1 代码路径 | 12 个仓库都去掉 v1 |
| SDK | 负责人 | 状态 | 备注 |
|---|---|---|---|
| .NET | Ori Lynn | pending | 参考实现 |
| Python | 待定 | pending | cryptography 库 |
| TypeScript | 待定 | pending | @peculiar/x509 |
| Java | 待定 | pending | Bouncy Castle |
| Rust | 待定 | pending | x509-parser + rcgen |
| Go | 待定 | pending | stdlib crypto/x509 |
agent-identity EKU 的 v2
证书;Agent 展示;verifier 接受。NIP-CERT-EKU-MISSING。NIP-CERT-SUBJECT-NID-MISMATCH。agent-01 happy path 端到端。2026-04-27 prototype 后修订(详见 §9.2):wire-size 上限放宽到 1600 字节(prototype 实测 1512 B);删除 50 µs 绝对值目标,仅保留 ≤ 5× regression 比率(绝对延迟受宿主硬件影响过大;prototype 在测 试容器内 v1 baseline 已经远超 50 µs,绝对目标不可移植)。新增 “CA Server 二进制 ≤ 2×” 推迟到 daemon / nip-ca-server 多语言移植期再测。
Prototype 落地于 feat/rfc-0002-x509-acme-prototype,按顺序交付:
impl/dotnet/src/NPS.NIP/X509/{NpsX509Oids,NipX509Builder,NipX509Verifier,Ed25519X509SignatureGenerator}.cs。
tests/NPS.Tests/Nip/X509/NipX509Tests.cs 5 个用例覆盖 round-trip、
EKU 缺失拒绝、subject/NID 不匹配拒绝、v1↔v2 跨格式共存。agent-01 challenge ——
impl/dotnet/src/NPS.NIP/Acme/{AcmeJws,AcmeMessages,AcmeServer,AcmeClient}.cs +
tests/NPS.Tests/Nip/Acme/AcmeAgent01Tests.cs。两个用例覆盖 agent-01
端到端发证 + 篡改签名负面路径返回 NIP-ACME-CHALLENGE-FAILED。agent-01(这是本 RFC 自己提的非标准 challenge 类型);用
pebble 验证 ACME 标准合规只能加 HTTP-01 的覆盖,相对 agent-01
本身的端到端证明价值边际很小。tools/pebble/setup.sh 已经放好
二进制下载脚本,留给后续 PR。下表数据由 NPS.Benchmarks.NipCert 产出
(dotnet run -c Release --project impl/dotnet/benchmarks/NPS.Benchmarks.NipCert -- --emit),
报告写入 docs/benchmarks/nip-cert-prototype.md。
| 指标 | 基线 (v1) | 提议 (v2) | 差值 | 方法 |
|---|---|---|---|---|
IdentFrame JSON 体积 |
459 B | 1512 B | +1053 B (+229%) | JsonSerializer.Serialize(IdentFrame) 的 UTF-8 字节数 |
| 验签延迟(2000 次平均) | 597.8 µs | 1698.5 µs | 2.84× | Stopwatch 计时 verifier.VerifyAsync(frame) |
§8.4 两条阈值因此被 prototype 数据修订:
Prototype 流程是先 X.509,后叠 ACME。耗时:X.509 + verifier + 测试 ~5
工作日;ACME(client + in-process server + agent-01 + 测试)~2 工作日。
pebble HTTP-01 interop 如果可做要再 ~1 天,被 agent-01 不被 pebble
理解阻塞。
OQ-1 推荐:X.509 + ACME 一起做(即原”both together”立场)。一旦 X.509 issuance 接好,ACME 大部分是机械工作;拆开会强迫做两轮跨语言 SDK 移植,把 ACME 一并合进同一个 acceptance 是更经济的做法。
1.3.6.1.4.1.99999 已退役,全部 NpsX509Oids 常量改以
1.3.6.1.4.1.65715 为根。任何在 1.3.6.1.4.1.99999.* 下签发的证书
MUST 在被用于合规测试、生产 NID 签发或跨组织互通之前吊销并在新 arc
下重新签发。PEN 落定后,本 RFC 由 Draft 推进至 Proposed。spec/NPS-3-NIP.md —— 当前 NIP 规范tools/nip-ca-server/README.md —— 当前 CA Server| 日期 | 作者 | 变更 |
|---|---|---|
| 2026-04-27 | Claude (prototype) | 用 feat/rfc-0002-x509-acme-prototype(.NET prototype + NPS.Benchmarks.NipCert)实测数据回填 §9 实测数据。OQ-1 闭环(X.509 + ACME 一起做)。§8.4 阈值修订(体积 1200 → 1600 B;删除绝对延迟目标,保留比率)。 |
| 2026-04-21 | Ori Lynn | 初稿 |