PEM 证书解码器

把 PEM 里的 base64 解回 DER,再按 ASN.1 的 TLV 逐层读出 X.509 字段:版本、序列号、签发者、有效期、主体、公钥位数与全部扩展。长度字段按长格式解析(本例的 tbsCertificate 头是 30 82 04 DF),扩展里 OID 之后的可选 critical 布尔值先跳过再取值。默认示例是 www.liuquan.net 的 Let's Encrypt 证书,解出 v3、RSA-4096 公钥、2048 位签名、有效期 90 天、10 个扩展。

PEM 只是 DER 的 base64 加首尾标记,没有任何加密;把证书贴给第三方等于公开了公钥与域名清单。
subject = CN=www.liuquan.net issuer = C=US, O=Let's Encrypt, CN=YR1 version v3 serial 0612EC0D242E63FC3674ED1FAE19EE4921D6 sigAlg sha256WithRSAEncryption sig 2048 位 validity 2026-09-25T14:11:46Z -> 2026-12-24T14:11:45Z(90 天) pubkey rsaEncryption RSA-4096 e=65537 mod[8] D98B17C67FD07D6F extensions 10: 1 keyUsage [critical] -> digitalSignature + keyEncipherment(未用位 5) 2 extKeyUsage -> 10 字节 3 basicConstraints [critical] -> CA=FALSE 4 subjectKeyIdentifier -> 20 字节 5 authorityKeyIdentifier -> 22 字节 6 authorityInfoAccess -> 37 字节 7 subjectAltName -> DNS:www.liuquan.net 8 certificatePolicies -> 10 字节 9 CRLDistributionPoints -> 37 字节 10 SCT -> 249 字节

默认示例是 www.liuquan.net 的 Let's Encrypt 证书。PEM 内容与预览字节数任意一项改动即重新解码,光标停在字节数框里按回车同样触发一次;导出 JSON 不带字节序标记,导出 CSV 带。

使用方法

  1. 输入框默认已经放好 www.liuquan.net 的 Let's Encrypt 证书;改动内容或点「解码 PEM」立刻重算,框里按 Ctrl+Enter(Mac 上 Cmd+Enter)同样触发。万一被清空,解码时会自动填回这一份。
  2. 只认 PEM:以 -----BEGIN CERTIFICATE----- 开头、-----END CERTIFICATE----- 结尾,中间是 base64,常见后缀有 .pem、.crt、.cer。DER 二进制与 .p7b 证书链要先转成 PEM 再贴。
  3. 「模数与扩展值十六进制预览字节数」默认 8,允许 4 到 64,步长 4,决定模数、密钥标识这些十六进制串截取多长;光标停在这个框里按回车触发一次。
  4. 结果卡片给出主体、签发者、有效期天数、公钥位数、扩展条数五项,下面那段文本按 X.509 的字段顺序逐项列出,扩展在最末尾编号排列。
  5. 清单下面是字段树表,标出每项的字节数与取值,默认显示 6 行,点「加载更多」展开全部 12 行,点「导出 CSV」存成带字节序标记的表格文件。
  6. 「复制结果」复制整段文本清单;「复制对照表」复制正文里那张扩展清单,可直接粘进表格或文档。
  7. 「导出 JSON」把所有字段写成一个 JSON 文件,不带字节序标记,脚本可以直接读;要表格时再点「导出 CSV」。
  8. 缺首尾标记、base64 长度不是 4 的倍数、顶层不是 SEQUENCE、长度字段越界,各自给出针对该条的提示,不会拿上一次的结果冒充本次输出。

这个 PEM 证书解码器能解决什么问题

第一类是拿到一份证书却不知道它签给谁、哪天到期。系统里双击 .crt 只能看到摘要,本页把它拆到字段:默认这份的 subject 是 CN=www.liuquan.net,有效期从 2026-09-25T14:11:46Z 到 2026-12-24T14:11:45Z,一共 90 天,到期前十几天就该换证。

第二类是不确定证书里的公钥是 RSA-2048 还是 RSA-4096。公钥 BIT STRING 有 527 字节,去掉第一个「未用位」字节后是 RSAPublicKey,它的第一个 INTEGER 有 513 字节,去掉补位符号位后 512 字节,即 4096 位。签名却只有 2048 位,因为签名由 CA 的密钥产生,和本站自己的密钥长度无关。

第三类是排查浏览器报的域名不匹配。真正参与主机名校验的是 subjectAltName 而不是 CN,本例 SAN 只有一条 DNS:www.liuquan.net,所以根域与别的子域都不会通过,需要另外签发或使用通配符条目。

第四类是想确认有没有写入 Certificate Transparency 日志。扩展列表第 10 条的 OID 是 1.3.6.1.4.1.11129.2.4.2,值 249 字节,这就是 SCT 列表,说明签发时把证书提交给了公开日志。

这个工具在本站也叫「Certificate解码PEM」,两种叫法指的是同一件事。下面先讲 PEM 与 DER 的对应关系,再讲 TLV 该怎么读。

计算原理与变量说明

PEM 不是加密,它只是 DER 的 base64 加上首尾两行标记。本例去掉标记后是 2044 个 base64 字符,2044 ÷ 4 = 511 组,每组 3 字节得 1533 字节,末尾两个等号各少算一字节,正好 1531 字节。补位只出现在最后一组,所以「组数 × 3 减填充个数」这个算法对任何 PEM 都成立。

DER 的最小单元是 TLV:一个标记字节、一段长度、一段内容。长度有两种写法:小于 128 时长度就是一个字节;否则这个字节的高位为 1,低 7 位表示后面还跟着几个长度字节。本例 tbsCertificate 的头是 30 82 04 DF,0x82 表示后面 2 个字节是长度,0x04DF = 1247;顶层头是 30 82 05 F7,长度 1527。只按低 7 位读会把 1247 读成 4,后面所有字段整体错位。

Certificate 是三层嵌套:最外层 SEQUENCE 里依次是 tbsCertificate、signatureAlgorithm、signatureValue。tbsCertificate 内部的顺序也是固定的:version、serialNumber、signature、issuer、validity、subject、subjectPublicKeyInfo,之后才是可选的 issuerUniqueID、subjectUniqueID 与 [3] extensions。version 藏在 [0] 隐式标签里的 INTEGER 中,本例是 A0 03 02 01 02,整数值 2 加 1 才是 v3。

Name 的编码分三层:Name 是 SEQUENCE OF RDN,每个 RDN 是 SET OF AttributeTypeAndValue,后者是 SEQUENCE { OID, 值 }。所以 C=US 里 C 是 OID 2.5.4.6,值是 PrintableString;O=Let's Encrypt 用 2.5.4.10,CN 用 2.5.4.3。本例 issuer 的三个 RDN 依次解出 C=US、O=Let's Encrypt、CN=YR1。

时间有两种编码。UTCTime 是 YYMMDDHHMMSSZ,标记 0x17,年份只有两位,YY ≥ 50 归 19xx,否则归 20xx;GeneralizedTime 是 YYYYMMDDHHMMSSZ,标记 0x18,2049 年之后的证书必须用它。本例两个时间都是 0x17,各自 13 个字符。

扩展是最容易读错的一段。Extension ::= SEQUENCE { extnID OID, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING },critical 取默认值 FALSE 时按 DER 规则必须省略,因此第二个子节点可能是 BOOLEAN(标记 0x01)也可能直接就是 OCTET STRING。先判断第二个子节点的标记是不是 0x01,是就跳过去再取值。本例 10 条扩展里 keyUsage 与 basicConstraints 带 critical,其余 8 条不带。

keyUsage 的值是 BIT STRING,第一个字节是「未用位数」,本例是 5,也就是说只有 3 个有效位。位序从字节的高位往低位数:第 0 位是 digitalSignature,第 1 位是 nonRepudiation,第 2 位是 keyEncipherment,本例置位的是第 0 与第 2 位,整字节是 0xA0。直接按字节值去查表会把位序读反。

公钥位数要从 RSAPublicKey 的第一个 INTEGER 算起。INTEGER 用补码表示,最高位为 1 时前面补一个 0x00 当作符号位,本例 513 字节去掉这 1 字节得 512 字节,乘 8 就是 4096 位;第二个 INTEGER 只有 3 字节,解出公开指数 65537。模数从符号位之后开始数,前 8 字节是 D98B17C67FD07D6F。

完整示例

默认这份证书:去标记后 2044 个 base64 字符,解出 1531 字节 DER;顶层头 30 82 05 F7,长度 1527;tbsCertificate 头 30 82 04 DF,长度 1247。三个顶层子节点的长度依次是 1247、13、257,加上各自的头正好铺满 1527 字节。

version 字段是 A0 03 02 01 02,[0] 里的 INTEGER 是 02,加 1 得 v3。serialNumber 是 18 字节的 0612EC0D242E63FC3674ED1FAE19EE4921D6,144 位已经达到 CA/B 论坛对随机序列号的熵要求。

issuer 解出 C=US, O=Let's Encrypt, CN=YR1;subject 只有一条 CN=www.liuquan.net。validity 里两个 0x17 时间解出 2026-09-25T14:11:46Z 与 2026-12-24T14:11:45Z,两者相差 90 天整。

公钥部分是 rsaEncryption,BIT STRING 527 字节(1 字节未用位加 526 字节内容),里面 RSAPublicKey 的两个 INTEGER 分别是 513 与 3 字节;513 去掉补位符号位得 512 字节即 4096 位,模数前 8 字节 D98B17C67FD07D6F,公开指数 65537。签名算法 OID 1.2.840.113549.1.1.11 即 sha256WithRSAEncryption,签名值 2048 位。

扩展共 10 条:keyUsage(critical,digitalSignature + keyEncipherment,未用位 5)、extKeyUsage、basicConstraints(critical,CA=FALSE)、subjectKeyIdentifier、authorityKeyIdentifier、authorityInfoAccess、subjectAltName(DNS:www.liuquan.net)、certificatePolicies、CRLDistributionPoints、SCT(1.3.6.1.4.1.11129.2.4.2,249 字节)。

PEM 证书字段与扩展对照表

下表是默认证书里 10 条扩展的读取结果,critical 一栏来自 OID 之后那个可选的 BOOLEAN。

#OID名称critical值字节取值摘要
12.5.29.15keyUsage是4digitalSignature + keyEncipherment(未用位 5)
22.5.29.37extKeyUsage否1210 字节
32.5.29.19basicConstraints是2CA=FALSE
42.5.29.14subjectKeyIdentifier否222C6AE3ECE84E3E120DC1B48AD12A330D54025D56
52.5.29.35authorityKeyIdentifier否2422 字节
61.3.6.1.5.5.7.1.1authorityInfoAccess否3937 字节
72.5.29.17subjectAltName否19DNS:www.liuquan.net
82.5.29.32certificatePolicies否1210 字节
92.5.29.31CRLDistributionPoints否3937 字节
101.3.6.1.4.1.11129.2.4.2SCT 列表否252249 字节

字节账从 PEM 字符数一路算到扩展,每一步都能用上一行复核。

环节数量说明
PEM 主体2044 字符去掉首尾标记、去掉所有换行后的 base64
DER 编码1531 字节511 组 × 3 减 2 个填充位
顶层 SEQUENCE1527 字节头 30 82 05 F7,长格式两字节长度
tbsCertificate1247 字节头 30 82 04 DF,被签名的内容
signatureValue257 字节1 字节未用位加 256 字节,即 2048 位
扩展区 [3]540 字节里层承载 10 条扩展

DER 的常见标记与本例出现的位置,读十六进制时对着看。

标记类型本例位置
30SEQUENCE顶层、tbsCertificate、RDN、AlgorithmIdentifier
31SET每个 RDN 里的 AttributeTypeAndValue 集合
02INTEGERversion、serialNumber、RSA 模数与指数
03BIT STRINGsignatureValue、公钥、keyUsage
04OCTET STRING扩展的 extnValue、SCT 列表外层
06OBJECT IDENTIFIER算法与每个扩展的 extnID
17 / 18UTCTime / GeneralizedTimevalidity 的 notBefore 与 notAfter
A0 / A3[0] / [3] 上下文标签version 与 extensions

三张表合着看:第一张看扩展的 critical 与取值,第二张看字节从哪一步变到哪一步,第三张看标记与字段的对应。

结果边界、舍入与适用说明

常见问题

PEM 和 DER 有什么区别?手上只有 DER 怎么办?

两者装的是同一份 ASN.1 结构,PEM 是 DER 的 base64 再加上首尾标记,便于用文本方式传输;DER 是纯二进制。手上只有 .der 时先用 openssl 的 -inform DER -outform PEM 转换,或者看文件开头是不是 0x30,是二进制就说明不是 PEM。本页只接受 PEM。

为什么签名是 2048 位而公钥是 4096 位?

两者来自不同的密钥。签名值由签发 CA 的私钥产生,本例 CA 用的是 RSA-2048,所以 signatureValue 是 256 字节即 2048 位;公钥是本站自己的 RSA-4096,模数 513 字节去掉补位符号位得 512 字节即 4096 位。证书里的公钥长度与签名长度没有必然关系。

扩展里的 critical 到底起什么作用?

它表示「不认识这条扩展的实现必须拒绝这张证书」。keyUsage 标了 critical,是因为限制密钥用途属于安全约束;basicConstraints 标了 critical,是为了防止末端实体证书被当成 CA 用。而 SAN 只在 subject 为空时才被强制要求 critical,本例 subject 非空,所以它的 SAN 不带 critical 也合规。

为什么浏览器只看 subjectAltName 不看 CN?

RFC 6125 之后的通行做法是主机名只与 SAN 里的 dNSName 比对,CN 仅在 SAN 完全不存在时才作为回退,主流浏览器早已取消这个回退。本例 SAN 只有 DNS:www.liuquan.net,所以其余主机名一律不匹配,即使 CN 相同也不行。

长度字段的长格式会在哪里碰到?写错了会怎样?

只要内容超过 127 字节就会用到,本例 tbsCertificate 是 30 82 04 DF、顶层是 30 82 05 F7,都是两字节长度。如果只取长度字节的低 7 位,1247 会被读成 4,后面所有字段的偏移全部错位,解析要么立刻越界报错,要么解出一串毫无意义的值。

把证书贴到这个页面会不会泄露私钥?

不会泄露私钥,因为证书里根本不含私钥:证书只有公钥、主体、签发者与签名。但需要知道 PEM 本身没有加密,公钥与 SAN 里的域名清单是公开信息,贴给第三方等同于把这些公开出去。私钥在 .key 文件里,标记是 -----BEGIN PRIVATE KEY-----,本页遇到这种输入会报顶层不是 SEQUENCE。

这个工具免费吗?

免费,无需注册即可使用。

输入的数据安全吗?

安全。解码在当前浏览器本地完成,不会上传输入内容。

相关工具