NPS-Release

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 —

NPS-RFC-0002:NID 证书改用 X.509 + ACME

1. 摘要

把 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 吊销语义保持不变。

2. 动机

本 RFC 回应 2026-04-20 的评审意见:“CA 不如直接兼容现有的 CA 套路”

评论是对的。当前 NIP CA(tools/nip-ca-server-*)在操作模型上已经 复刻了经典 X.509 CA 的流程——CSR → 签发 → CRL/OCSP 吊销 → 续签—— 但用的是自研序列化。这带来两项不必要的成本:

  1. 工具链无法复用。 OpenSSL、rustls、BouncyCastle、step-ca、 HashiCorp Vault PKI、HSM 厂商、Kubernetes 的 cert-manager—— 没一个能直接签发、验证或托管 NIP 证书。6 种语言的 NIP CA Server 每一家都在重复实现同一套仪式。
  2. 签发协议无法复用。 通过自研 REST 接口手工签发 NID,享受不到 ACME 多年积累的运维成熟度:自动续签、staging 环境、公共 CA 速率 限制、Certbot/lego 客户端、Kubernetes operator。

改到 X.509 + ACME 的代价是暴露一个 ASN.1 解析面、证书体积 2–4 倍, 换来的是 15 年的 PKI 工具链复用;以及未来可以跑一个 “Let’s Encrypt for Agents” 的公共服务,或者把 NID 签发通过交叉签名委托给现有 CA。

3. 非目标

4. 详细设计

4.1 Wire 格式 / 帧变更

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。

4.2 Manifest / NWM 变更

无。X.509 证书链仍在 IdentFrame 里传,NWM 透明。

4.3 错误码

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-identitynode-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 挑战校验失败

4.4 状态机 / 流程

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 响应时间规范化,与此处无关。)

4.5 向后兼容性


5. 备选方案

5.1 保留自研格式,新增 X.509 导出垫片

保留现有证书格式;提供一个单向 X.509 导出以做互通(例如 nipc export --x509)。

5.2 只上 X.509,不上 ACME(保留 REST 签发)

用 X.509 但继续 /certs/issue 这类自研 REST 接口。

5.3 不动


6. 缺陷与风险


7. 安全性考量


8. 实施计划

8.1 阶段划分

阶段 范围 出口条件
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

8.2 SDK 覆盖矩阵

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

8.3 测试计划

  1. X.509 round-trip:CA 签发带 agent-identity EKU 的 v2 证书;Agent 展示;verifier 接受。
  2. EKU critical 强制:v2 证书缺 EKU → NIP-CERT-EKU-MISSING
  3. Subject / NID 不匹配:证书 Subject CN ≠ IdentFrame.nid → NIP-CERT-SUBJECT-NID-MISMATCH
  4. ACME agent-01 happy path 端到端。
  5. ACME 重放防护:重复挑战 token → 400。
  6. 跨格式:v1 client + v2 server;v2 client + v1 server (Phase 2 双向互通;Phase 3+ 双向干净拒绝)。

8.4 基准

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 多语言移植期再测。


9. 实测数据

9.1 Prototype 分支

Prototype 落地于 feat/rfc-0002-x509-acme-prototype,按顺序交付:

9.2 实测

下表数据由 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 数据修订:

9.3 §10 OQ-1 的含义

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 是更经济的做法。


10. 未决问题


11. 未来工作


12. 参考


附录 A. 修订记录

日期 作者 变更
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 初稿