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

> 用 OpenSSL 3.x 和 keytool 在 PEM、DER、PKCS#7（.p7b）、PKCS#12（.pfx）、Java 密钥库之间转换证书和私钥，以及如何判断文件格式。

Source: https://ssl.sh/zh/cookbook/convert-formats/
Last updated: 2026-11-10

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

为什么会有这么多格式，见[证书格式详解](https://ssl.sh/zh/learn/certificate-formats/)；PKCS#12 的参数和兼容性问题另有专页：[PKCS#12 命令](https://ssl.sh/zh/cookbook/pkcs12/)。不方便用命令行时，可以用[在线格式转换工具](https://ssl.sh/zh/tools/ssl-converter/)，转换在浏览器本地完成。

## 判断文件是什么格式

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

```bash
head -1 server.crt server.key bundle.p7b server.csr
file server.der example.pfx
openssl asn1parse -inform DER -in example.pfx | head -3
```

```text
==> 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` 输出同一张证书的二进制编码。

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

```text
709 server.crt
483 server.der
```

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

## DER 转 PEM

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

```bash
openssl x509 -in server.der -inform DER -out server.pem
head -1 server.pem
```

```text
-----BEGIN CERTIFICATE-----
```

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

## 私钥在 PEM 和 DER 之间转换

用 `openssl pkey`，所有算法通用。

```bash
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
```

```text
-----BEGIN PRIVATE KEY-----
```

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

## PEM 转 PKCS#7（.p7b）

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

```bash
openssl crl2pkcs7 -nocrl -certfile server.crt -certfile intermediate.crt -out bundle.p7b
head -1 bundle.p7b
```

```text
-----BEGIN PKCS7-----
```

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

## PKCS#7（.p7b）转 PEM

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

```bash
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
```

```text
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` 把私钥、证书和证书链合成一个带密码的文件。

```bash
openssl pkcs12 -export -inkey server.key -in server.crt -certfile intermediate.crt \
  -name "example.com" -out example.pfx
```

```text
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 命令](https://ssl.sh/zh/cookbook/pkcs12/)。

## PFX 转 PEM

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

```bash
openssl pkcs12 -in example.pfx -noenc -out example-all.pem
grep -E "BEGIN|friendlyName|subject=" example-all.pem
```

```text
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 服务器要的三个文件。

```bash
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
```

```text
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。

```bash
keytool -importkeystore \
  -srckeystore example.pfx -srcstoretype PKCS12 \
  -destkeystore example.jks -deststoretype JKS
```

```text
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。

```bash
keytool -importkeystore -srckeystore keystore.jks -srcstoretype JKS \
  -destkeystore keystore.p12 -deststoretype PKCS12
openssl pkcs12 -in keystore.p12 -noenc -out keystore.pem
```

```text
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

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

```bash
cat example.com.crt intermediate.crt > fullchain.pem
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout
```

```text
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](https://ssl.sh/zh/errors/nginx-key-values-mismatch/)；漏了中间证书，安卓、curl、Java 和微信小程序请求会报[缺少中间证书](https://ssl.sh/zh/errors/missing-intermediate-certificate/)。如果前一个文件末尾没有换行，`cat` 会把 `-----END CERTIFICATE----------BEGIN CERTIFICATE-----` 粘在一行，用 `grep -c BEGIN fullchain.pem` 数一下张数。
