MAVLink 安全通信系列(4):X.509 与身份认证
返回系列总目录。
本篇聚焦信任如何建立:证书将身份与公钥绑定,但验证签名只是接受决策的 一个环节。我们先说明 PKI/X.509 的模型与本项目验证器的边界,再以必要篇幅介绍 ECDH/ECDSA——因为证书本身不协商会话密钥,也不能证明对端当前持有私钥。
1. 握手与身份:要解决的问题
一个安全链路建立前,通信双方必须回答两个问题:
- 你是谁?(身份认证)—— 确认对端是可信设备,防冒充;
- 我们怎么得到一把只有你我共享的密钥?(密钥协商)—— 且最好每次会话都 换一把(前向保密);设备凭证应有清晰的生命周期与撤销流程。
这两件事往往在一次"握手"里打包完成。为此业界沉淀出一套工具箱:
flowchart TD
H["握手 / 身份认证"] --> PSK["PSK 预共享密钥<br/>简单但全网同钥"]
H --> SIG["挑战-应答 / 签名<br/>证明私钥持有"]
H --> DH["DH / ECDH 密钥协商<br/>各自算出共享密钥"]
H --> CERT["数字证书 X.509<br/>CA 签名 + 身份/公钥绑定"]
H --> PROTO["现成协议<br/>TLS / DTLS / Noise / WireGuard"]2. 可选路线横向对比
给 MAVLink 做身份 + 握手,业界路线与取舍(加粗为本项目选型):
| 方案 | 技术栈 | 优点 | 缺点 |
|---|---|---|---|
| MAVLink v2 signing | SHA-256 签名 + 共享密钥 | 官方内置、每帧 +13B、防篡改 | 只签名不加密、全网同钥、无前向保密、无法识别对端身份 |
| PSK + AES-GCM | 预共享 16B 密钥 | 加密 + 认证一次完成、实现小 | 全网同钥、单点泄露即全网泄露、无身份、无前向保密、轮换需重刷固件 |
| 完整 TLS 1.3 / DTLS | X.509 证书链 + ECDHE/ECDSA + AES-GCM + CRL/OCSP | 工业级成熟、双向认证 + 前向保密 + 在线吊销 | Flash/RAM/栈开销大、需独立网络栈、证书链与吊销查询复杂 |
| Noise 协议框架 | DH(25519/448) + AEAD + HKDF | 极轻、前向保密、形式化验证 | 裸公钥、无证书语义、难以逐台吊销、公钥需带外预置 |
| WireGuard | Curve25519 + ChaCha20-Poly1305 + BLAKE2s | 极轻极快、前向保密、内核级成熟 | 裸公钥、非证书;Curve25519 与 STM32 硬件 PKA 不匹配 |
| 简化 X.509(本项目) | P-256 ECDHE/ECDSA + 钉扎单 CA + AES-128-GCM + HKDF | 证书语义 + 双向认证 + 前向保密设计;复用 TUNNEL;约 15 KB | 需自建 CA 与签发流程;钉扎单 CA;无 CRL/OCSP 或逐台撤销检查 |
证书将身份属性与公钥绑定,并由 CA 签名;它本身不执行吊销。要回答"证书现在 是否仍可信",还需 CRL、OCSP、在线策略或本地 denylist 等撤销机制。本项目没有 这些机制,也不验证证书身份与 sysid 的对应关系,因此不能承诺逐台吊销。选择 简化 X.509 的理由是复用证书与 CA 信任模型,同时将实现缩小到单 CA、一级证书和 单套件;被裁掉的功能必须由部署策略承担,而不能默认为存在。
3. X.509 证书:一张"CA 签名的公钥凭据"
X.509 是数字证书的标准格式。证书本质是一段被 CA 私钥签名认证的数据,把 "某个身份"和"一把公钥"绑定在一起。它的结构(ASN.1):
Certificate ::= SEQUENCE {
tbsCertificate 待签名主体(下面这条)
├─ version 版本(v3)
├─ serialNumber 序列号(全局唯一)
├─ signature 签名算法标识
├─ issuer 签发者名称(= CA)
├─ validity 有效期 notBefore ~ notAfter
├─ subject 持有者名称(= 设备)
├─ subjectPublicKeyInfo 持有者公钥(关键!)
└─ extensions 扩展(可选)
signatureAlgorithm 签名算法标识(本项目 ecdsa-with-SHA256)
signatureValue 对 tbsCertificate 的 ECDSA 签名
}一条证书,两段核心逻辑:
- 签发:CA 用自己的私钥对 leaf 的
tbsCertificate做 ECDSA 签名,写入signatureValue; - 验证:持证人用 CA 公钥对
signatureValue验签,能同时确认两件事: ①tbsCertificate未被篡改(签名匹配);② 证书确实由该 CA 签发(只有 CA 持有对应私钥)。
完整 PKI 里证书可形成一条验证路径(根 CA → 中间 CA → 叶子)。本项目为嵌入式 裁剪为钉扎一个 CA 公钥,只验证直接由该 CA 签发的一级 leaf。证书本身只是 被签名的数据;信任来自验证方预置的信任锚和它执行的验证策略,而不是“证书看起来 合法”。
3.1 DER:证书的二进制编码
X.509 通常用 DER(Distinguished Encoding Rules)编码,规则是标准的 TLV(Tag-Length-Value):
┌──────┬──────────┬────────┐
│ Tag │ Length │ Value │
│(类型)│ (长度) │ (内容) │
└──────┴──────────┴────────┘嵌套 SEQUENCE 层层包裹。验证器就是一个带严格边界检查的 TLV 游标,逐层下钻。 相关 OID(签名 / 公钥算法标识):
ecdsa-with-SHA256 1.2.840.10045.4.3.2
id-ecPublicKey 1.2.840.10045.2.1
prime256v1 (P-256) 1.2.840.10045.3.1.7你在本文后面会看到,本项目
mavlink_x509.h的验证策略就是:严格读 TLV → 校验签名算法 OID → 定位subjectPublicKeyInfo(校验公钥算法 + 曲线参数)→ 校验曲线点 → 用钉扎的 CA 公钥验签 → 校验有效期。
3.2 验证证书不等于完成授权
一个可靠的证书接受决策要回答多个独立问题:
- 结构和签名是否有效,证书是否由受信任路径签发?
- 当前时间是否在有效期内,证书用途/约束是否允许该协议角色?
- 证书身份是否映射到预期的设备、链路或 MAVLink
sysid? - 证书是否被撤销,信任策略是否仍允许使用?
本项目验证器严格解析有限 DER 子集、验证指定算法与 P-256 公钥、使用钉扎 CA 公钥 验证一级 leaf;有效期只有传入时钟回调才检查。当前不检查 CRL/OCSP,不把 subject 绑定到 sysid,也不是通用 RFC 5280 路径验证器。因此,“同一 CA 签发”不能直接 等同于“该设备可授权执行任意 MAVLink 命令”。
4. 非对称密码的两把锤子:ECDH 与 ECDSA
握手里真正干活的是 ECDH(密钥协商)和 ECDSA(数字签名)。它们都建立在 椭圆曲线上的、"离散对数难解"这一数学难题之上。本项目用 NIST P-256 (prime256v1)曲线。
4.1 P-256 曲线
P-256 定义在素数域 GF(p) 上:
其中
基点 G 生成一个阶为 256 位素数 n 的循环子群(余因子为 1)。私钥是一个随机 标量 d ∈ [1, n-1],公钥是曲线上的点 Q = dG。给定 Q 反推 d 就是椭圆曲线 离散对数问题(ECDLP),计算上不可行——这是整个机制的安全根基。
4.2 ECDH:各自算出同一把共享密钥
sequenceDiagram
participant Alice
participant Bob
Note over Alice: 临时私钥 a,公钥 A = aG
Note over Bob: 临时私钥 b,公钥 B = bG
Alice->>Bob: 公开 A = aG
Bob->>Alice: 公开 B = bG
Note over Alice: S = a·B = a·(bG) = abG
Note over Bob: S = b·A = b·(aG) = abG
Note over Alice,Bob: 双方得到同一共享点 S,取其 x 坐标(32B)作为密钥素材演算:aB = a·(bG) = abG = b·(aG) = bA。被动窃听者只看到 A、B,计算共享点在 计算上不可行;但未认证的 ECDH 无法阻止主动中间人分别与两端协商两把密钥。 因此必须把临时公钥和双方身份绑定到经过认证的握手 transcript。若临时私钥每次 新生成、强随机、使用后清除,且长期身份密钥仅用于握手认证,则长期身份私钥日后 泄露时,历史会话通常仍无法恢复,这就是前向保密(PFS)的条件性保证。
4.3 ECDSA:数字签名 / 验签
签名证明"我持有与公钥 Q 配对的私钥 d",且签名内容不可篡改。流程:
flowchart TB
subgraph SIGN["签名(持有私钥 d)"]
Z2["z = SHA256(消息)"] --> K2["选 nonce k(RFC6979 确定性派生)"]
K2 --> R2["R = kG,r = R.x mod n"]
R2 --> S2["s = k⁻¹·(z + r·d) mod n"]
S2 --> SIG["签名 = (r, s),共 64 字节"]
end
subgraph VERIFY["验签(持有公钥 Q)"]
Z3["z = SHA256(消息)"] --> W3["w = s⁻¹ mod n"]
W3 --> U3["u₁ = z·w,u₂ = r·w"]
U3 --> P3["R' = u₁·G + u₂·Q"]
P3 --> CHK{"R'.x mod n == r ?"}
CHK -->|是| OK["验签通过"]
CHK -->|否| FAIL["验签失败"]
end关键公式:
- 验证依据:只有确实持有
d的人才能造出使R'与R重合的(r,s); - RFC6979 确定性 nonce:实现可从私钥与消息摘要确定性地产生签名随机量
k, 避免签名过程直接依赖运行时 RNG;这不降低保护长期私钥的要求,也不替代 ECDHE 临时私钥所需的密码学随机性。
本项目
mavlink_ecc.h即实现了这两把锤子:P-256 模运算、Montgomery ladder 点乘、点有效性校验、ECDH、RFC6979 确定性 ECDSA、HMAC-SHA256 与常量时间比较。
5. 证书验证 + 签名自证:为什么握手要两步
一个常见疑问:证书都验过了,为什么还要再来一次 ECDSA 签名自证?
因为证书是公开数据,可以被任何人复制转发。攻击者截获到合法设备的证书后, 照样能把这张证书"重放"给对端,冒充合法设备。所以光验证书只能证明"这张证书 是真的",不能证明"对端就是证书的主人"。
于是握手分两步:
flowchart LR
A["① 验证书<br/>CA 公钥验签 + 提取对端公钥"] --> B["② 签名自证<br/>对端用私钥对握手 transcript 做 ECDSA 签名"]
B --> C["用提取的公钥验签<br/>证明"对端持有证书对应私钥""]- 第①步拿到"可信的对端公钥";
- 第②步对端用私钥对握手过程(transcript)签名,我方用第①步的公钥验签, 证明"它确实持有私钥"——拷贝证书没私钥,过不了这关。
这就是"证书(身份)+ 签名(持有证明)"缺一不可的原因。
6. 为什么选 P-256 ECDHE + ECDSA + 简化 X.509
逐条对着需求做取舍:
- 要"可治理的机群身份" → 选证书:证书把身份与公钥绑定,便于签发、轮换和 建立撤销流程;但本项目当前不实现 CRL/OCSP,不能自动判定证书已吊销;
- 要"前向保密" → 选 ECDHE:每次会话临时密钥协商,长期私钥泄露不殃及历史;
- 要"嵌入式可接受" → 砍到最小:去掉证书链、CRL/OCSP、套件协商、会话票据, 只保留"一个钉扎 CA + 一级证书 + 一套套件";
- 要"复用 MAVLink 通道" → 握手数据塞进标准 TUNNEL 消息,不引入独立网络栈;
- 选 NIST P-256 而非 Curve25519 → STM32 的硬件 PKA/AES 加速器面向 NIST P-256,且 P-256 的 X.509 生态(openssl/cryptography 生成证书)最成熟。
体积账:P-256 公钥 65 字节、ECDSA 签名 64 字节、证书 DER 约 440 字节——分几个 TUNNEL 帧就能传完,纯软件、无堆分配、无外部依赖。
7. 小结与下一篇
- 身份 + 握手要解决"你是谁"和"我们如何共享一把密钥",可选路线有意在简化的 X.509;
- X.509 = CA 签名绑定的"身份←→公钥"凭据;撤销检查、用途约束和设备 ID 映射 不是“证书格式”自动提供的属性;
- ECDH 提供前向保密的密钥协商,ECDSA 提供身份签名自证,都建立在 P-256 上;
- 未认证 ECDH 易受中间人攻击;PFS 也以安全随机数和临时秘密清除为前提;
- "验证书 + 签名自证"两步缺一不可(证书可复制,私钥不可复制)。
下一篇《X.509 与 MAVLink 握手集成》专注协议 本身:握手记录如何通过 TUNNEL 承载、transcript 如何被签名/Finished 认证,以及 状态机如何处理重传和失败。ECDH、ECDSA 与 HKDF 在本文讲原理,下一篇只讨论其 协议上下文与实现调用,避免重复推导。
延伸阅读与规范
- IETF, RFC 5280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile;
- IETF, RFC 6979: Deterministic Usage of DSA and ECDSA;
- NIST, SP 800-56A Rev. 3: Pair-Wise Key-Establishment Schemes。
以上文档帮助理解标准概念;本项目的简化证书验证器不等于完整 RFC 5280 路径验证, 具体差异以第 5 篇的验证边界和源码为准。
