格式转换与 PKCS#12

OpenSSL 证书格式转换:PEM、DER、P7B、PFX、JKS

作者: · 最后核实 实测 macOS · OpenSSL 3.6.1(keytool 命令未实跑)

证书格式之间的区别只在于“怎么包装同一份数据”:PEM 是 Base64 文本,DER 是二进制,PKCS#7 打包多张证书,PKCS#12 把私钥和证书打包在一起,JKS 是 Java 自己的密钥库。下面每种转换都是一条 openssl 或 keytool 命令,证书内容不会因此改变。OpenSSL 输出来自 3.6.1;本机没有 JDK,keytool 的输出是 JDK 17 以上版本的典型提示。

为什么会有这么多格式,见证书格式详解;PKCS#12 的参数和兼容性问题另有专页:PKCS#12 命令。不方便用命令行时,可以用在线格式转换工具,转换在浏览器本地完成。

判断文件是什么格式

先看第一行,二进制文件再用 file 或 asn1parse 确认。

head -1 server.crt server.key bundle.p7b server.csr
file server.der example.pfx
openssl asn1parse -inform DER -in example.pfx | head -3
==> server.crt <==
-----BEGIN CERTIFICATE-----
==> server.key <==
-----BEGIN PRIVATE KEY-----
==> bundle.p7b <==
-----BEGIN PKCS7-----
==> server.csr <==
-----BEGIN CERTIFICATE REQUEST-----
server.der:  Certificate, Version=3
example.pfx: data
    0:d=0  hl=4 l=1723 cons: SEQUENCE
    4:d=1  hl=2 l=   1 prim: INTEGER           :03
    7:d=1  hl=4 l=1641 cons: SEQUENCE
第一行或内容 格式 常见扩展名
-----BEGIN CERTIFICATE----- PEM 证书 .pem .crt .cer
-----BEGIN PRIVATE KEY----- / RSA PRIVATE KEY / EC PRIVATE KEY PEM 私钥(PKCS#8 / PKCS#1 / SEC1) .key .pem
-----BEGIN ENCRYPTED PRIVATE KEY----- 加密的 PKCS#8 私钥 .key
-----BEGIN PKCS7----- PEM 格式的 PKCS#7 证书包 .p7b .p7c
二进制,file 显示 Certificate DER 证书 .der .cer
二进制,asn1parse 先是 INTEGER :03 再是 pkcs7-data PKCS#12 .pfx .p12

PKCS#12 文件以版本号 3 开头,asn1parse 就是靠这个和 DER 证书区分的。Windows 上没有 file 命令,可以用 Git Bash 或 WSL 执行。

PEM 转 DER

-outform DER 输出同一张证书的二进制编码。

openssl x509 -in server.crt -outform DER -out server.der
ls -l server.crt server.der | awk '{print $5, $NF}'
709 server.crt
483 server.der

去掉 Base64 后体积约为原来的四分之三。Windows、Java 和很多嵌入式设备、网络设备都接受 DER 格式的 .cer。一个 DER 文件只能装一张证书,证书链要逐张转换或者改用 PKCS#7。

DER 转 PEM

-inform DER 读取二进制输入,输出默认就是 PEM。

openssl x509 -in server.der -inform DER -out server.pem
head -1 server.pem
-----BEGIN CERTIFICATE-----

OpenSSL 3.x 多数情况下能自动识别 DER,但脚本里保留 -inform 以兼容 1.1.1。报 wrong tag 或 not enough data 时,文件多半是 PKCS#7 或 PKCS#12,而不是单张 DER 证书。

私钥在 PEM 和 DER 之间转换

用 openssl pkey,所有算法通用。

openssl pkey -in server.key -outform DER -out server.key.der
openssl pkey -in server.key.der -inform DER -out server.key.pem
head -1 server.key.pem
-----BEGIN PRIVATE KEY-----

输出是 PKCS#8;目标程序要 BEGIN RSA PRIVATE KEY 时加 -traditional。DER 私钥主要给 Java(PKCS8EncodedKeySpec)和部分硬件用,Web 服务器和宝塔面板都要 PEM。

PEM 转 PKCS#7(.p7b)

crl2pkcs7 -nocrl 把一张或多张 PEM 证书打成 PKCS#7 包。

openssl crl2pkcs7 -nocrl -certfile server.crt -certfile intermediate.crt -out bundle.p7b
head -1 bundle.p7b
-----BEGIN PKCS7-----

加 -outform DER 生成二进制 .p7b,Windows 导入更稳。PKCS#7 永远不含私钥;Windows/IIS 用它表示“证书链”,Tomcat 往已有密钥库导入 CA 回执时也接受它。

PKCS#7(.p7b)转 PEM

pkcs7 -print_certs 把里面每张证书都解成 PEM。

openssl pkcs7 -in bundle.p7b -print_certs -out chain.pem
# 二进制 .p7b
openssl pkcs7 -in bundle.p7b -inform DER -print_certs -out chain.pem
grep -E "^subject|^issuer" chain.pem
subject=CN=example.com
issuer=CN=Example Test Intermediate CA
subject=CN=Example Test Intermediate CA
issuer=CN=Example Test Root CA

subject=、issuer= 行是注释,Nginx 和 Apache 会忽略。当证书链用之前检查顺序:服务器证书必须排第一。

PEM 转 PFX(PKCS#12)

pkcs12 -export 把私钥、证书和证书链合成一个带密码的文件。

openssl pkcs12 -export -inkey server.key -in server.crt -certfile intermediate.crt \
  -name "example.com" -out example.pfx
Enter Export Password:
Verifying - Enter Export Password:

IIS、Azure、Windows 证书导入和不少 Java 服务都要这个格式。OpenSSL 3.0 起默认用 AES-256-CBC 加密、SHA-256 做 MAC,Windows Server 2016 及更早版本、老版本 Java 和 macOS 可能导入失败,这时加 -legacy。详见 PKCS#12 命令。

PFX 转 PEM

pkcs12 -noenc 把私钥和全部证书写进一个 PEM 文件。

openssl pkcs12 -in example.pfx -noenc -out example-all.pem
grep -E "BEGIN|friendlyName|subject=" example-all.pem
Enter Import Password:
    friendlyName: example.com
subject=CN=example.com
-----BEGIN CERTIFICATE-----
subject=CN=Example Test Intermediate CA
-----BEGIN CERTIFICATE-----
    friendlyName: example.com
-----BEGIN PRIVATE KEY-----

-noenc(3.0 起,之前叫 -nodes)表示私钥不加密输出,不加会要求你再设一个 PEM 密码。报错里出现 unsupported 和 RC2-40-CBC,说明 PFX 用的是旧算法(比如从老版 Windows 导出),加 -legacy 即可。

把 PFX 拆成私钥、证书、证书链

用 -nocerts、-clcerts、-cacerts 分别取出 Web 服务器要的三个文件。

openssl pkcs12 -in example.pfx -nocerts -noenc -out example.com.key
openssl pkcs12 -in example.pfx -clcerts -nokeys -out example.com.crt
openssl pkcs12 -in example.pfx -cacerts -nokeys -out chain.crt
Enter Import Password:
Enter Import Password:
Enter Import Password:

加 -passin file:pass.txt 免去三次输入;云厂商 IIS 证书包里的 .txt 就是 PFX 密码。输出开头的 Bag Attributes 行无害,但宝塔面板等控制台可能不认,可以重写到新文件去掉它:openssl pkey -in example.com.key -out clean.key、openssl x509 -in example.com.crt -out clean.crt。

PFX 转 Java 密钥库(JKS)

keytool -importkeystore 把 PKCS#12 里的条目复制到 JKS。

keytool -importkeystore \
  -srckeystore example.pfx -srcstoretype PKCS12 \
  -destkeystore example.jks -deststoretype JKS
Importing keystore example.pfx to example.jks...
Entry for alias example.com successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled

别名来自 PFX 的友好名(-name)。较新的 JDK 会警告 JKS 是专有格式:Java 9 起默认密钥库类型已是 PKCS12,Tomcat、Spring Boot、Jetty 都能直接加载 .pfx(keystoreType="PKCS12"),很多时候根本不需要 JKS。

JKS 转 PFX 和 PEM

反过来先用 keytool 转成 PKCS#12,再交给 OpenSSL。

keytool -importkeystore -srckeystore keystore.jks -srcstoretype JKS \
  -destkeystore keystore.p12 -deststoretype PKCS12
openssl pkcs12 -in keystore.p12 -noenc -out keystore.pem
Importing keystore keystore.jks to keystore.p12...
Entry for alias tomcat successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled

密钥库里有多个条目时用 -srcalias 只导出一个。keytool 的 PKCS12 密钥库只有一个密码,原 JKS 的私钥密码和库密码不同时,导出时会让你统一。

拼接 fullchain.pem

把服务器证书和中间证书按顺序拼起来,服务器证书在前,不放根证书。

cat example.com.crt intermediate.crt > fullchain.pem
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout
subject=CN=example.com
issuer=CN=Example Test Intermediate CA

subject=CN=Example Test Intermediate CA
issuer=CN=Example Test Root CA

Nginx 的 ssl_certificate、Apache 2.4.8 以上的 SSLCertificateFile、宝塔面板的“证书(PEM 格式)”框都填这个内容。顺序反了 Nginx 会报 key values mismatch;漏了中间证书,安卓、curl、Java 和微信小程序请求会报缺少中间证书。如果前一个文件末尾没有换行,cat 会把 -----END CERTIFICATE----------BEGIN CERTIFICATE----- 粘在一行,用 grep -c BEGIN fullchain.pem 数一下张数。

常见问题

.crt 文件是 PEM 还是 DER?

都有可能。.crt、.cer、.pem 这些扩展名只是习惯,不代表格式。用记事本打开或执行 head -1:以 -----BEGIN CERTIFICATE----- 开头的是 PEM,一堆乱码的是 DER。

云厂商下载的证书包里为什么有好几个文件夹?

阿里云、腾讯云等平台按服务器类型打包:Nginx 目录里是 PEM 格式的证书(通常已拼好中间证书)和私钥,Apache 目录把证书和链分成两个文件,IIS 目录是 .pfx 加一个密码文件,Tomcat 目录是 .jks 或 .pfx。选对文件夹就不用自己转换。

.p7b 能直接转成 .pfx 吗?

只有手里还有私钥才行。.p7b 里只有证书:先用 openssl pkcs7 -print_certs 取出 PEM,再用 openssl pkcs12 -export -inkey 你的私钥 生成 .pfx。

资料来源

  1. OpenSSL 3.0 手册:openssl-format-options
  2. OpenSSL 3.0 手册:openssl-crl2pkcs7
  3. OpenSSL 3.0 手册:openssl-pkcs12
  4. RFC 7468:PKIX 结构的文本编码
  5. Oracle Java SE 21:keytool