PEM(Privacy-Enhanced Mail)是 OpenSSL 和大多数 Linux/Unix 系统中最常用的证书和私钥格式。理解 PEM 的内部结构有助于排查格式转换问题、编写自动化脚本,以及深入理解 PKI 工作原理。

PEM 格式概述

PEM 是一种基于 Base64 编码的格式,用于存储DER编码的二进制数据。其核心特点:

  • 文本友好:内容为可打印的 ASCII 字符,便于复制粘贴
  • 结构化:使用 -----BEGIN xxx----------END xxx----- 标记包裹
  • Base64 编码:将 DER 二进制数据转换为 ASCII 字符

PEM 文件结构

典型的 PEM 文件结构如下:

1
2
3
4
-----BEGIN TYPE-----
Base64 编码的 DER 数据
(每行 64-76 字符)
-----END TYPE-----

实际示例:X.509 证书

1
2
3
4
5
6
-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIUE7Few6gydfFoBDQ+1aAHba6absYwDQYJKoZIhvcNAQEL
BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNjA3MTYxNjA4MDFaFw0yNjA3MTcxNjA4
MDFaMA8xDTALBgNVBAMMBFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
...
-----END CERTIFICATE-----

实际示例:私钥(PKCS#8 格式)

1
2
3
4
5
6
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDwyIPC1HgFgg7v
RcV/Lbxy/zb7j2hVcnZgOjg5sb6HbTLa18MfoFDhQzOfyYwTlrcE2MdUhST9VyDa
4Y+iWxY3XL6xZHaNNf7s0bWgxUwc2Z9sx2hCBaHP7zndwipcgE1n4BH82sRTtCbn
...
-----END PRIVATE KEY-----

实际示例:PKCS#1 格式 RSA 私钥

1
2
# 使用 -traditional 参数生成
openssl genrsa -traditional -out key.pem 2048
1
2
3
4
5
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvT0G582+zwdzDDHH9oCOprlkyB1htiKySMWvxR30CZHeUhlF
Qr37x0WxG9Fa6XJPWN8mOV1jLFRvORhizynQsss8O/XdUsOVwEwlY0UgEzvuG0hd
...
-----END RSA PRIVATE KEY-----

实际示例:证书签名请求(CSR)

1
2
3
4
5
-----BEGIN CERTIFICATE REQUEST-----
MIICZjCCAU4CAQAwITENMAsGA1UEAwwEVGVzdDEQMA4GA1UECgwHVGVzdE9yZzCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPDIg8LUeAWCDu9FxX8tvHL/
...
-----END CERTIFICATE REQUEST-----

实际示例:PKCS#7 / CMS 签名数据

1
2
3
4
5
-----BEGIN PKCS7-----
MIIFeAYJKoZIhvcNAQcCoIIFaTCCBWUCAQExDzANBglghkgBZQMEAgEFADALBgkq
hkiG9w0BBwGgggMDMIIC/zCCAeegAwIBAgIUE7Few6gydfFoBDQ+1aAHba6absYw
...
-----END PKCS7-----

Base64 编码与 DER 的关系

PEM 实际上是 DER 格式的 Base64 表示。让我们验证这个关系:

1
2
3
4
5
6
7
8
# 查看 PEM 文件大小
$ ls -l server.crt
-rw-r--r-- 1 root root 1099 Jul 17 00:08 server.crt

# 提取 Base64 内容并解码,对比 DER 大小
$ cat server.crt | sed -n '/-----BEGIN/,/-----/p' | \
  grep -v "BEGIN\|END" | base64 -d | wc -c
771

结论:PEM 文件(约 1099 字节)比原始 DER 数据(约 771 字节)大约 43%,这是 Base64 编码的开销(每 3 字节变成 4 字符)。

解码验证

1
2
3
4
5
# 将 PEM 解码为 DER 格式
openssl x509 -in cert.pem -outform DER -out cert.der

# 使用 openssl asn1parse 解析 DER 内容
openssl asn1parse -in cert.der -inform DER

输出示例:

1
2
3
4
5
    0:d=0  hl=4 l= 767 cons: SEQUENCE          
    4:d=1  hl=4 l= 231 cons: SEQUENCE          
    8:d=2  hl=2 l=   3 cons: INTEGER           02:03:01:02
   11:d=2  hl=2 l=  20 cons: INTEGER           13:B1:5E:C3:A8:32:75:F1:68:04:34:3E:D5:A0:07:6D:AE
...

常见的 PEM 类型标识

类型标识 内容 OpenSSL 命令
CERTIFICATE X.509 证书 openssl x509
CERTIFICATE REQUEST CSR openssl req
RSA PRIVATE KEY PKCS#1 RSA 私钥 openssl rsa -traditional
PRIVATE KEY PKCS#8 私钥 openssl pkcs8
ENCRYPTED PRIVATE KEY 加密的 PKCS#8 私钥 -
PKCS7 PKCS#7 签名数据 openssl smime
CMS CMS 签名数据(PKCS#7 的 RFC 名称) openssl cms
EC PARAMETERS EC 曲线参数 openssl ecparam
DH PARAMETERS DH 参数 openssl dhparam

多证书 PEM 文件

PEM 格式支持在一个文件中包含多个证书(证书链):

1
2
3
4
5
# 创建证书链文件
cat server.crt intermediate.crt root.crt > chain.pem

# OpenSSL 可以正确读取
openssl verify -CAfile root.crt -untrusted intermediate.crt server.crt

文件结构示例:

1
2
3
4
5
6
7
8
9
-----BEGIN CERTIFICATE-----
[服务器证书]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[中间证书]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[根证书]
-----END CERTIFICATE-----

格式转换实战

DER 转 PEM

1
2
3
4
5
# 证书 DER -> PEM
openssl x509 -in cert.der -inform DER -out cert.pem

# 私钥 DER -> PEM (PKCS#8)
openssl pkcs8 -topk8 -inform DER -in key.der -out key.pem

PEM 转 DER

1
2
3
4
5
# 证书 PEM -> DER
openssl x509 -in cert.pem -outform DER -out cert.der

# 私钥 PEM -> DER
openssl pkcs8 -topk8 -in key.pem -outform DER -out key.der

PKCS#1 与 PKCS#8 私钥互转

1
2
3
4
5
# PKCS#1 (RSA PRIVATE KEY) -> PKCS#8 (PRIVATE KEY)
openssl pkcs8 -topk8 -in key-pkcs1.pem -out key-pkcs8.pem

# PKCS#8 -> PKCS#1 (使用 -traditional)
openssl rsa -in key-pkcs8.pem -traditional -out key-pkcs1.pem

加密与解密私钥

1
2
3
4
5
# 加密私钥(AES-256-CBC)
openssl pkcs8 -topk8 -in key.pem -out key-encrypted.pem -v1 PBE-SHA1-3DES

# 解密私钥
openssl pkcs8 -topk8 -in key-encrypted.pem -out key-decrypted.pem

查看 PEM 文件信息

方法一:使用 openssl

1
2
3
4
# 自动检测格式并显示
openssl x509 -in cert.pem -noout -text
openssl rsa -in key.pem -noout -text
openssl req -in req.pem -noout -text

方法二:提取并解码 Base64

1
2
3
4
5
6
7
# 提取 Base64 内容
sed -n '/-----BEGIN/,/-----/p' input.pem | \
  grep -v "BEGIN\|END" | base64 -d > output.der

# 手动检查文件类型
file output.der
# 输出: output.der: X.509 certificate

方法三:检查文件头

1
2
3
# 快速检查 PEM 类型
head -1 input.pem
# 输出: -----BEGIN CERTIFICATE-----

常见问题排查

问题 1:PEM 格式不被支持

某些应用需要特定格式(如 Apache 旧版本需要 PKCS#1):

1
2
3
4
5
# 检查当前格式
head -1 yourkey.pem

# 如果是 PKCS#8,转换为 PKCS#1
openssl rsa -in yourkey.pem -traditional -out legacy-key.pem

问题 2:证书链顺序错误

验证失败可能是证书顺序错误:

1
2
3
4
# 正确顺序:服务器证书 -> 中间证书 -> 根证书
# 检查当前顺序
openssl crl2pkcs7 -nocrl -certfile CHAIN.pem | \
  openssl pkcs7 -print_certs -noout

问题 3:Base64 解码失败

检查 PEM 文件是否有非法字符:

1
2
# 检查非 Base64 字符
grep -vE '^[A-Za-z0-9+/=]+$' your.pem

总结

特性 说明
编码方式 Base64
原始格式 DER(ASN.1 编码的二进制)
文件扩展名 .pem, .crt, .cer, .key, .csr
多证书支持 是(串联存放)
加密支持 是(通过 PKCS#8 加密私钥)

理解 PEM 格式的内部结构,能够帮助你在遇到证书相关问题时快速定位根因,特别是在处理格式转换和证书链验证场景时。


参考来源: