FMO(NFM Over Internet) 数字证书格式
本文声明 FMO 数字证书体系中 根证书(Root CA)与中间证书(Intermediate CA) 的开放格式,供实现互通的设备、服务器与第三方开发者参考。格式与官方证书工具 fmo-certificate-tools 的签发实现保持一致。
1. 证书体系
FMO 的身份体系建立在三层信任链之上:
Root CA(根机构) → Intermediate CA(中间机构) → User Cert(用户证书)
| 证书 | 职责 | 由谁签发 |
|---|---|---|
| Root CA | 信任锚,只负责签发中间证书;全网唯一的认证源头 | 自签名 |
| Intermediate CA | 负责审核真实操作证材料,签发用户证书 | Root CA |
| User Cert | 声明"这个呼号、这个 UID、这把公钥"属于被认可用户 | Intermediate CA |
核心原则:
- 证书是公开对象。呼号、UID、公钥、序列号、有效期都可以被公开缓存、转发和再分发;证书本身不属于秘密数据。
- 私钥永不进入证书。证书只承载公钥,签发方私钥与持有方私钥都不出现在任何证书字段中。
- 信任是逐级传递的:
Root CA 的公钥 验证 中间证书,中间证书的公钥 验证 用户证书,直至把某条广播、某次连接绑定到一个具体呼号。
2. 通用约定
2.1 编码与时间
- 所有证书 JSON 使用 UTF-8 编码。
- 时间戳统一使用 UTC Unix 时间戳,单位为秒。
- 二进制字段(公钥、签名等)在 JSON 中统一使用 不带 padding 的 base64url 编码;进入签名时使用原始字节。
- 所有大小写敏感字段必须精确匹配。
2.2 签名算法
- 证书签名算法固定为 Ed25519。
- 签名的输入不是 JSON 文本,而是按协议字段顺序构造的固定顺序语义值数组,再对该数组做确定性 CBOR 编码后得到的原始字节串。
- 确定性 CBOR 编码不允许 indefinite length,不允许同一语义值存在多种编码形式;不同实现的 JSON 缩进、空格、字段排序差异不影响签名结果。
signature字段本身永远不进入被签名数组。
2.3 域分离
所有证书签名结构必须显式包含以下前缀字段,避免不同对象之间误验签:
["FMO", 4, "messageType", ...]
protocol固定为"FMO"version固定为整数4messageType为当前对象类型,如rootCA、intermediateCA、userCert
接收端必须在完全一致的 protocol、version、messageType 条件下重建签名数组,再做确定性 CBOR 编码后验签。
2.4 字段类型规则
| 语义 | 签名数组中的表示 |
|---|---|
sn、issuerSn、uid、iat、exp、uidRange.start/end |
CBOR integer |
isCA |
CBOR boolean(不是 1/0 文本) |
publicKey |
原始 32 字节的 CBOR byte string |
name、email、keyId、crl、license |
CBOR text string |
issuingCountries |
先全部转为两位大写国家码,再按字典序升序排序,作为嵌套 text string array |
3. 根证书(Root CA)格式
3.1 职责与特性
- Root CA 是整条信任链的信任锚,私钥离线保存,不得在线使用。
- Root CA 为自签名证书,用自身
subject.publicKey对应的私钥签发。 - 完整 Root 自签名证书通过 OTA 写入设备信任存储;设备运行时以其中的 Root 公钥作为信任锚,Root 不通过 APRS 广播,也不在激活响应中下发。
- 默认有效期为 10 年。
3.2 JSON 结构
{
"sn": 1,
"type": "rootCA",
"issuer": {
"name": "BG5ESN",
"email": "xifengzui@yeah.net"
},
"subject": {
"name": "BG5ESN",
"publicKey": "root_public_key_base64url"
},
"extensions": {
"isCA": true,
"pathLen": 1,
"crl": "https://bg5esn.com/share/ca/root_crl.json",
"license": "https://bg5esn.com/share/ca/license.md",
"keyId": "1"
},
"iat": 1780876683,
"exp": 2096495883,
"signatureAlgorithm": "Ed25519",
"signature": "root_self_signature_base64url"
}
3.3 字段定义
| 字段 | 说明 |
|---|---|
sn |
根证书序列号,全局唯一 |
type |
固定为 rootCA |
issuer.name |
签发机构名称 |
issuer.email |
签发机构联系邮箱 |
subject.name |
主体名称(默认同签发机构) |
subject.publicKey |
Root 公钥,32 字节 base64url |
extensions.isCA |
固定为 true |
extensions.pathLen |
固定为 1,允许签发一级中间 CA |
extensions.crl |
Root CRL 可下载地址 |
extensions.license |
使用许可声明地址 |
extensions.keyId |
根密钥标识 |
iat |
生效时间(UTC Unix 秒) |
exp |
过期时间(UTC Unix 秒) |
signatureAlgorithm |
固定为 Ed25519 |
signature |
Root 自签名,64 字节 base64url |
3.4 签名数组(15 元素)
["FMO", 4, "rootCA", sn, issuerName, issuerEmail, subjectName, subjectPublicKeyBytes, isCA, pathLen, crl, license, keyId, iat, exp]
说明:
- Root 虽为自签名证书,签名数组仍按
issuer与subject逻辑字段分别展开,不做省略。 subjectPublicKeyBytes为 Root 公钥的原始 32 字节。isCA进入数组时为 CBOR booleantrue;pathLen为整数1。signature字段本身不进入该数组。
4. 中间证书(Intermediate CA)格式
4.1 职责与特性
- Intermediate CA 由 Root CA 签发,负责审核真实操作证材料并签发用户证书。
- 证书通过
extensions声明授权边界:可签发的 UID 范围(uidRange)与可签发的国家码范围(issuingCountries)。 pathLen固定为0,表示不允许继续下发下级 CA。- 默认有效期为 5 年,且不得超出签发给它的 Root 证书的有效期。
4.2 JSON 结构
{
"sn": 1001,
"type": "intermediateCA",
"issuer": {
"sn": 1,
"name": "BG5ESN",
"publicKey": "root_public_key_base64url"
},
"subject": {
"name": "BD4WPW",
"email": "bd4wpw@example.com",
"publicKey": "intermediate_public_key_base64url"
},
"extensions": {
"isCA": true,
"pathLen": 0,
"keyId": "1",
"crl": "https://bd4wpw.example.com/crl.json",
"license": "https://bd4wpw.example.com/license.md",
"uidRange": {
"start": 5000,
"end": 10000
},
"issuingCountries": [
"CN"
]
},
"iat": 1780876801,
"exp": 1938643203,
"signatureAlgorithm": "Ed25519",
"signature": "signed_by_root_base64url"
}
4.3 字段定义
| 字段 | 说明 |
|---|---|
sn |
中间证书序列号,全局唯一 |
type |
固定为 intermediateCA |
issuer.sn |
签发它的 Root 证书序列号 |
issuer.name |
Root 的主体名称 |
issuer.publicKey |
Root 公钥(与 Root 证书 subject.publicKey 一致) |
subject.name |
中间机构名称 |
subject.email |
中间机构联系邮箱 |
subject.publicKey |
中间 CA 公钥,32 字节 base64url |
extensions.isCA |
固定为 true |
extensions.pathLen |
固定为 0,禁止签发下级 CA |
extensions.keyId |
中间密钥标识 |
extensions.crl |
该中间 CA 的 Intermediate CRL 可下载地址 |
extensions.license |
使用许可声明地址 |
extensions.uidRange.start |
可签发 UID 下限(含) |
extensions.uidRange.end |
可签发 UID 上限(含) |
extensions.issuingCountries |
可签发国家码列表,两位大写 |
iat |
生效时间(UTC Unix 秒) |
exp |
过期时间(UTC Unix 秒) |
signatureAlgorithm |
固定为 Ed25519 |
signature |
Root 私钥签名,64 字节 base64url |
4.4 签名数组(20 元素)
["FMO", 4, "intermediateCA", sn, issuerSn, issuerName, issuerPublicKeyBytes, subjectName, subjectEmail, subjectPublicKeyBytes, isCA, pathLen, keyId, crl, license, uidRangeStart, uidRangeEnd, [countryCode1, countryCode2, ...], iat, exp]
说明:
issuerPublicKeyBytes与subjectPublicKeyBytes都使用原始公钥字节,不是 base64url 展示文本。issuingCountries作为已按字典序升序排序的嵌套数组整体进入签名数组;任一实现排序结果不同都会导致签名不一致,因此排序规则必须严格一致。isCA为 CBOR booleantrue;pathLen为整数0。signature字段本身不进入该数组。
4.5 签发约束
Root 签发中间证书时必须满足以下条件,任何一项不满足即为非法证书:
| 约束 | 规则 |
|---|---|
| issuer 身份 | issuer.sn == root.sn 且 issuer.name == root.subject.name 且 issuer.publicKey == root.subject.publicKey |
| 时间窗口 | iat ≥ root.iat 且 exp ≤ root.exp |
| 授权边界 | uidRange.start ≤ uidRange.end;issuingCountries 非空 |
5. 证书链校验
接收端校验证书链时按自顶向下顺序执行:
1. 用本地信任存储中的 Root 公钥,验证 Intermediate CA 证书签名
2. 核对 Intermediate 的 issuer 三要素与 Root 一致(sn / name / publicKey)
3. 检查时间窗口:root.iat ≤ intermediate.iat,intermediate.exp ≤ root.exp
4. 用 Intermediate 的 subject.publicKey,验证 User Cert 签名
5. 核对 User Cert 的 issuerSn == intermediate.sn
6. 检查用户证书时间窗口:intermediate.iat ≤ user.iat,user.exp ≤ intermediate.exp
7. 检查 user.uid 落在 intermediate.uidRange 范围内
8. 检查从 callsign 推断的国家码落在 intermediate.issuingCountries 范围内(可推断时)
说明:User Certificate 只承载呼号、UID、公钥与有效期,是其后续 APRS 广播与业务报文签名的根绑定材料。其完整格式与 APRS 紧凑表示将另行声明。
6. 吊销(CRL)
证书吊销遵循"上级吊销下级"的原则:
| CRL | 吊销对象 | 命中规则 |
|---|---|---|
| Root CRL | Intermediate CA | sn + certFingerprint 双命中 |
| Intermediate CRL | User Cert | uid + certFingerprint 双命中 |
certFingerprint 为被吊销证书的 SHA-256 指纹,计算规则:
certFingerprint = SHA-256( 该证书签名数组的确定性 CBOR 编码字节串 )
说明:
- 不允许对证书 JSON 文本直接做 SHA-256。
- 不允许把
signature字段并入指纹计算输入。 - 仅
sn/uid命中而指纹不匹配时不算吊销命中,这通常意味着证书重签发或缓存混乱。
CRL 是公开、带 Ed25519 签名、可独立下载与验签的对象;吊销明细格式将另行声明。
7. 工具与参考
- 官方证书工具:fmo-certificate-tools(签发、验证、CRL、密钥对生成)
- 信任链模型:FMO(NFM Over Internet) 的信任链模型
- 语音数据协议:FMO(NFM Over Internet) 语音数据开放协议
本文档定义的帧结构与签名规则由 FMO 项目方主导,不接受外部 PR 变更申请;演进建议请通过 FMO 项目方渠道提出,保证全网互操作性。