使用方法
- 输入框默认已经放好 www.liuquan.net 的 Let's Encrypt 证书;改动内容或点「解码 PEM」立刻重算,框里按 Ctrl+Enter(Mac 上 Cmd+Enter)同样触发。万一被清空,解码时会自动填回这一份。
- 只认 PEM:以 -----BEGIN CERTIFICATE----- 开头、-----END CERTIFICATE----- 结尾,中间是 base64,常见后缀有 .pem、.crt、.cer。DER 二进制与 .p7b 证书链要先转成 PEM 再贴。
- 「模数与扩展值十六进制预览字节数」默认 8,允许 4 到 64,步长 4,决定模数、密钥标识这些十六进制串截取多长;光标停在这个框里按回车触发一次。
- 结果卡片给出主体、签发者、有效期天数、公钥位数、扩展条数五项,下面那段文本按 X.509 的字段顺序逐项列出,扩展在最末尾编号排列。
- 清单下面是字段树表,标出每项的字节数与取值,默认显示 6 行,点「加载更多」展开全部 12 行,点「导出 CSV」存成带字节序标记的表格文件。
- 「复制结果」复制整段文本清单;「复制对照表」复制正文里那张扩展清单,可直接粘进表格或文档。
- 「导出 JSON」把所有字段写成一个 JSON 文件,不带字节序标记,脚本可以直接读;要表格时再点「导出 CSV」。
- 缺首尾标记、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 | 值字节 | 取值摘要 |
|---|---|---|---|---|---|
| 1 | 2.5.29.15 | keyUsage | 是 | 4 | digitalSignature + keyEncipherment(未用位 5) |
| 2 | 2.5.29.37 | extKeyUsage | 否 | 12 | 10 字节 |
| 3 | 2.5.29.19 | basicConstraints | 是 | 2 | CA=FALSE |
| 4 | 2.5.29.14 | subjectKeyIdentifier | 否 | 22 | 2C6AE3ECE84E3E120DC1B48AD12A330D54025D56 |
| 5 | 2.5.29.35 | authorityKeyIdentifier | 否 | 24 | 22 字节 |
| 6 | 1.3.6.1.5.5.7.1.1 | authorityInfoAccess | 否 | 39 | 37 字节 |
| 7 | 2.5.29.17 | subjectAltName | 否 | 19 | DNS:www.liuquan.net |
| 8 | 2.5.29.32 | certificatePolicies | 否 | 12 | 10 字节 |
| 9 | 2.5.29.31 | CRLDistributionPoints | 否 | 39 | 37 字节 |
| 10 | 1.3.6.1.4.1.11129.2.4.2 | SCT 列表 | 否 | 252 | 249 字节 |
字节账从 PEM 字符数一路算到扩展,每一步都能用上一行复核。
| 环节 | 数量 | 说明 |
|---|---|---|
| PEM 主体 | 2044 字符 | 去掉首尾标记、去掉所有换行后的 base64 |
| DER 编码 | 1531 字节 | 511 组 × 3 减 2 个填充位 |
| 顶层 SEQUENCE | 1527 字节 | 头 30 82 05 F7,长格式两字节长度 |
| tbsCertificate | 1247 字节 | 头 30 82 04 DF,被签名的内容 |
| signatureValue | 257 字节 | 1 字节未用位加 256 字节,即 2048 位 |
| 扩展区 [3] | 540 字节 | 里层承载 10 条扩展 |
DER 的常见标记与本例出现的位置,读十六进制时对着看。
| 标记 | 类型 | 本例位置 |
|---|---|---|
| 30 | SEQUENCE | 顶层、tbsCertificate、RDN、AlgorithmIdentifier |
| 31 | SET | 每个 RDN 里的 AttributeTypeAndValue 集合 |
| 02 | INTEGER | version、serialNumber、RSA 模数与指数 |
| 03 | BIT STRING | signatureValue、公钥、keyUsage |
| 04 | OCTET STRING | 扩展的 extnValue、SCT 列表外层 |
| 06 | OBJECT IDENTIFIER | 算法与每个扩展的 extnID |
| 17 / 18 | UTCTime / GeneralizedTime | validity 的 notBefore 与 notAfter |
| A0 / A3 | [0] / [3] 上下文标签 | version 与 extensions |
三张表合着看:第一张看扩展的 critical 与取值,第二张看字节从哪一步变到哪一步,第三张看标记与字段的对应。
结果边界、舍入与适用说明
- 输入为空时会把默认的 www.liuquan.net 证书填回框里再解码,并在提示区说明这一步,不会静默套用上一次的结果。
- 没有 -----BEGIN CERTIFICATE----- 或 -----END CERTIFICATE----- 时分别报出缺哪一行,并提示 DER 与 .p7b 需要先转换。
- base64 主体长度不是 4 的倍数时报出具体字符数,说明可能多粘或少粘了字符;出现集合外字符时报出检查方向。
- 顶层第一个字节不是 0x30 时报出实际值并提示可能贴成了私钥或 CSR;顶层子节点不足 3 个时报出实际个数。
- 长度字段声明的字节数超出实际长度时报「DER 可能被截断」,长度字节超过 4 个时同样报错,不做猜测式解析。
- 预览字节数不在 4 到 64 之间时报出允许范围,不自动取整;模数不足该长度时按实际长度截断,不补零。
- 有效期天数按两个时刻的毫秒差除以 86400000 后四舍五入到整天,本例是 90 天;不足一天按 0 天计。
- 只解析单个证书。一个 PEM 里有多张证书时只取第一张 BEGIN 到第一张 END 之间的内容,中间链证书不展开。
- 证书链是否可信、是否吊销不在本页判断范围:这里只读结构,不联网取 CRL 或 OCSP,也不校验证书签名。
- 页面不写本地存储,也不上传输入内容,刷新后回到默认示例。
常见问题
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。
这个工具免费吗?
免费,无需注册即可使用。
输入的数据安全吗?
安全。解码在当前浏览器本地完成,不会上传输入内容。