上一篇文章讲清了 PKI 的整体框架:CA 把身份与公钥绑定在数字证书中,用户再沿着信任链验证这层绑定关系。
但当我们真正拿到一张证书时,看到的往往不是“持有者、公钥、有效期”这些整齐的字段,而是一段这样的文本:
或者干脆是一份编辑器无法正常打开的二进制文件。
这些内容与证书字段之间是什么关系?为什么同一张证书既可以保存成 .der,也可以保存成 .pem?Base64 是不是一种加密?要回答这些问题,需要把数字证书拆成四层:
这四层分别解决“数据是什么”“如何变成字节”“如何放进文本环境”和“这段文本是什么类型”的问题。
上图展示的是数字证书最常见的处理方式。RFC 7468 允许文本编码包装 BER 数据,但强烈推荐使用 DER;实际证书工具通常采用 DER。
一、ASN.1:只描述结构,不负责保存数据
ASN.1(Abstract Syntax Notation One,抽象语法记法)是一套与编程语言无关的数据描述语言。
它的作用有些像接口定义或数据结构声明:只规定一个对象由哪些字段组成、字段是什么类型、哪些字段可以省略,不关心 Java、Go 或 C 具体用什么类来实现。
ASN.1 将常用类型分为两类:
| 类型 | 常见成员 | 在证书中的用途 |
|---|---|---|
| 基本类型 | INTEGER、BIT STRING、OCTET STRING、OBJECT IDENTIFIER、各种字符串和时间类型 | 序列号、公钥、签名值、算法标识、名称和有效期 |
| 结构类型 | SEQUENCE、SET、SEQUENCE OF、SET OF、CHOICE | 把多个字段组合成证书、名称、扩展项等复杂结构 |
例如,X.509 证书最外层可以简化为:
这段定义表达了三个事实:
- 一张证书是一个有序结构
SEQUENCE; tbsCertificate是待签名的证书正文;- 正文之外还保存了签名算法和 CA 生成的签名值。
这里的 TBS 是 To Be Signed 的缩写。CA 真正签名的对象不是我们在证书查看器中看到的格式化文本,而是 tbsCertificate 编码后得到的那段确定字节。
ASN.1 是证书的“结构图纸”,不是证书文件本身。只有经过编码规则处理后,它才会变成可以保存和传输的字节。
二、BER 与 DER:同一份结构如何变成字节
ASN.1 只定义结构,还需要一套规则把结构转换成二进制。BER、DER、CER 和 PER 都是 ASN.1 的编码规则,其中 PKI 最常见的是 DER。
BER 为什么还不够
BER(Basic Encoding Rules)允许同一个值存在多种合法编码方式。例如,某些长度可以使用不同的表示方法,结构既可以提前写明长度,也可以用结束标记表示内容结束。
普通数据交换只要双方都能解码,多种表示方式未必是问题;数字签名却要求更加严格。
签名计算面对的是字节序列。假设两个编码都表达同一个证书结构,但最终字节不同,那么它们计算出的摘要和签名也会不同。接收方重新编码时只要选择了另一种合法形式,验签就会失败。
DER 的核心价值是“唯一”
DER(Distinguished Encoding Rules)是 BER 的受限子集。它通过更严格的规则,要求同一个 ASN.1 值只能得到一种规范编码,例如:
- 必须使用确定长度;
- 长度必须采用最短合法形式;
- 基本类型和结构类型必须采用规定的编码模式;
- 集合成员必须按规范顺序编码。
因此,DER 真正解决的不是“能不能编码”,而是“能不能得到唯一字节”。
对密码系统而言,唯一编码非常重要:结构相同、编码就相同;编码相同、摘要和签名才有稳定的验证基础。
三、TLV:DER 字节流的阅读方法
DER 编码通常可以按 TLV 来理解:
- Tag:说明当前值是什么类型;
- Length:说明后面的内容占多少字节;
- Value:保存实际内容;如果当前值是结构类型,里面还会继续嵌套其他 TLV。
常见的 Tag 如下:
| 十六进制 | ASN.1 类型 | 常见用途 |
|---|---|---|
02 | INTEGER | 证书序列号、RSA 大整数 |
03 | BIT STRING | 公钥、证书签名值 |
04 | OCTET STRING | 扩展项包装、二进制数据 |
06 | OBJECT IDENTIFIER | 算法、属性和扩展项标识 |
0C | UTF8String | UTF-8 字符串 |
13 | PrintableString | 可打印字符名称 |
17 | UTCTime | RFC 5280 要求证书有效期在 2049 年及以前使用该类型 |
18 | GeneralizedTime | RFC 5280 要求证书有效期从 2050 年起使用该类型 |
30 | SEQUENCE / SEQUENCE OF | 有序复合结构 |
31 | SET / SET OF | 集合结构 |
一个 OID 的编码示例
对象标识符 OID 用一串数字唯一标识算法、名称属性或证书扩展项。例如 1.2.840.113549 是 RSA/PKCS 命名空间的一部分。
它经过 DER 编码后是:
第一段 06 表示这是 OID,第二段 06 表示内容长度为 6 字节,后面的字节才是 OID 各节点压缩后的值。
为什么正整数前面有时会多一个 00
DER 中 INTEGER 使用最高位表示正负。如果一个正整数编码后的首字节最高位恰好为 1,就必须在前面补一个 00,避免它被解释为负数。
十进制 128 的十六进制值是 80,完整 TLV 不是 02 01 80,而是:
这也是解析证书序列号或 RSA 大整数时,经常会看到前导零的原因。它不是数据被随意补齐,而是在保持数值符号正确。
四、X.500 Name:证书名称为何是层层嵌套的结构
证书签发者 issuer 和持有者 subject 都使用 X.500 的 Name 类型。它不是一段简单字符串,而是多层结构:
我们平时看到的名称:
实际表达的是多个“属性类型 + 属性值”。其中 C、O、CN 只是便于人阅读的简称,编码中使用的是 OID:
| 简称 | 含义 | OID |
|---|---|---|
C | 国家或地区 | 2.5.4.6 |
O | 组织 | 2.5.4.10 |
OU | 组织单元 | 2.5.4.11 |
CN | 通用名称 | 2.5.4.3 |
理解这层结构后,就能解释一些开发中容易误判的问题:
- DN 不是普通逗号分隔字符串,不能只靠字符串切割解析;
- 属性顺序、集合规则、字符串类型和空格规范都可能影响比较;
- 同一个显示名称不等于同一个证书主体;
issuer + serialNumber用于唯一标识一张证书;subject和subjectAltName表达证书绑定的主体身份;- Key Usage、Extended Key Usage 和证书策略等扩展项限定证书用途;
- 信任链、有效期和吊销状态共同决定这张证书当前是否可以信任。
五、Base64 与 PEM:只是包装,不是加密
DER 得到的是二进制数据。二进制适合程序处理,却不方便放进邮件、配置文件或只接受文本的传输协议,于是有了 Base64。
Base64 把每 3 个字节转换成 4 个可打印字符;末尾不足 3 个字节时使用 = 填充。因此编码后的体积通常会增加约三分之一。
Base64 只改变表示形式:
- 不需要密钥;
- 可以无损还原;
- 不提供保密性;
- 不提供完整性;
- 任何人都能解码。
通常所说的 PEM,更准确地讲是 RFC 7468 定义的 PKIX 文本编码:它把 ASN.1 编码结果转换为 Base64,并在外层增加类型标签。
不同标签代表不同对象:
在最常见的 DER 场景中,DER 决定二进制内容,Base64 负责文本化,PEM 负责标明边界和对象类型。RFC 7468 也允许包装 BER,但强烈推荐 DER。表示形式的转换不会改变证书字段、签名或信任关系。
文件扩展名只能作为提示,不能作为可靠判断依据。.cer 既可能是 DER 二进制,也可能是 PEM 文本;最直接的办法是查看开头是否存在 -----BEGIN,或交给证书工具识别。
六、从文件到字段:实际如何查看证书
OpenSSL 可以分别查看 PEM 和 DER 证书:
如果希望观察 TLV 的嵌套结构,可以使用:
这些命令只负责解析和展示证书,不会自动证明证书可信。需要验证证书链时,应显式提供信任锚和中间证书:
这条 verify 命令用于构建并验证证书链,但默认不会执行 OCSP 查询;如果要检查 CRL,还需提供 CRL 数据并显式启用 -crl_check 或 -crl_check_all。因此命令返回成功也不能单独证明证书当前未被吊销。
asn1parse 输出中的偏移、头部长度和内容长度对应 DER 结构的嵌套关系与 TLV 边界;verify 在链验证失败时则会显示出错证书的深度。遇到证书解析或验证失败时,可以按以下顺序排查:
- 文件究竟是 PEM 还是 DER;
- PEM 的头尾标签是否匹配,Base64 是否完整;
- DER 的 Length 是否与 Value 长度一致;
- ASN.1 类型与协议定义是否一致;
- 证书签名算法、正文签名算法是否一致;
- 解析成功后,再检查有效期、用途、吊销状态和信任链。
“能解析”只说明编码结构合法,不代表证书可信;“签名值正确”也只说明内容未被签发后修改,还需要验证签发者是否受信任、证书是否在有效期内、是否已被吊销、用途是否允许当前操作。
七、把四层关系重新串起来
现在再看一张 X.509 证书,它的形成过程就很清楚了:
这里最值得记住的不是某个 Tag 的具体数值,而是每一层的职责:
ASN.1 管结构,DER 管唯一字节,Base64 管文本表示,PEM 管对象边界。
分清这四层之后,PEM、DER、.cer、Base64 就不再是互相混杂的文件名词,而是一条从抽象证书结构到实际文件的编码流水线。
下一篇可以沿着这条流水线继续向里走:拆开 tbsCertificate,逐项理解版本号、序列号、签发者、持有者、公钥信息,以及 X.509 v3 扩展项如何限制一张证书“能做什么”。
参考资料
- ITU-T X.690:ASN.1 的 BER、CER 与 DER 编码规则
- RFC 5280:Internet X.509 公钥基础设施证书与 CRL Profile
- RFC 7468:PKIX 文本编码
- RFC 4648:Base-N 编码
- OpenSSL x509 与 OpenSSL asn1parse 官方文档
系列文章
- 上一篇:从密码学历史到公钥基础设施
- 本篇:从 ASN.1 到 DER,读懂数字证书的编码方式
笔记整理自《PKI_CA与数字证书技术大全》第二部分“PKI 技术基础”,并结合第三部分的 X.509 证书格式重新组织。
