chundev
日期:2026-03-31
憑證檔案的本質就是兩種東西:編碼格式(文字 or 二進位)和內容類型(只有憑證 or 含私鑰)。副檔名經常騙人,用 file 和 openssl 指令才能確認真實格式。CA Chain 的正確順序是 Leaf → Intermediate → Root,用 Subject/Issuer 比對就能驗證。
不全是,但大部分是。 憑證世界只有兩種編碼格式:
| 格式 | 本質 | 用文字編輯器打開 | 特徵 |
|---|---|---|---|
| PEM | Base64 文字 | ✅ 看得到內容 | 以 -----BEGIN CERTIFICATE----- 開頭 |
| DER | 二進位 | ❌ 看到亂碼 | 原始的 ASN.1 二進位編碼 |
PEM 和 DER 的關係:
DER(二進位) → Base64 編碼 → 加上 header/footer → PEM(文字)
也就是說,PEM = DER 的 Base64 文字版。兩者承載的資訊完全一樣,只是表達方式不同。
PEM 格式長這樣:
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJALaGSC3J9gL4MA0GCSqGSIb3DQEBCwUA
MDExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ0w
... (Base64 encoded binary data) ...
CwYDVQQHDARTYW4gRnJhbmNpc2NvMB4XDTE4MDEwMTAwMDAwMFoX
-----END CERTIFICATE-----
中間那堆看起來像亂碼的東西,其實是二進位資料經過 Base64 編碼後的結果。用
openssl x509 -text就能把它解碼成人類看得懂的資訊。
這是最容易搞混的地方。副檔名代表的是「慣例」,不是「保證」。
| 副檔名 | 通常的編碼 | 內容 | 說明 |
|---|---|---|---|
.pem |
PEM(文字) | 憑證、私鑰、或兩者都有 | 最通用的副檔名,什麼都可能裝 |
.crt |
PEM 或 DER | 憑證(不含私鑰) | Linux/macOS 常用 |
.cer |
PEM 或 DER | 憑證(不含私鑰) | Windows 常用,和 .crt 本質一樣 |
.key |
PEM(文字) | 私鑰 | 以 -----BEGIN PRIVATE KEY----- 或 -----BEGIN RSA PRIVATE KEY----- 開頭 |
.der |
DER(二進位) | 憑證 | 明確標示為 DER 格式 |
.p12 / .pfx |
二進位(PKCS#12) | 憑證 + 私鑰 + CA 鏈,有密碼保護 | Windows/IIS 愛用,macOS 也認 |
.p7b / .p7c |
PEM 或 DER(PKCS#7) | 只有憑證鏈,不含私鑰 | Windows/Java 環境常見 |
.jks |
二進位(Java Keystore) | Java 專用的金鑰庫 | 舊版 Java 用,新版建議改用 PKCS#12 |
.csr |
PEM(文字) | 憑證簽署請求 | 以 -----BEGIN CERTIFICATE REQUEST----- 開頭 |
.crt 和 .cer 是同一種東西,只是不同平台的命名習慣.p12 和 .pfx 是同一種東西,只是不同年代的命名習慣.pem 是萬用副檔名,裡面可能是憑證、私鑰、或全部塞在一起當你拿到一個憑證檔案,用以下流程判斷它是什麼:
file 指令初判file certificate.*
輸出範例:
certificate.pem: PEM certificate # → PEM 格式
certificate.der: data # → 可能是 DER 二進位
certificate.pfx: data # → 可能是 PKCS#12
certificate.p7b: data # → 可能是 PKCS#7
head 看一眼head -5 certificate.crt
| 看到什麼 | 代表什麼 |
|---|---|
-----BEGIN CERTIFICATE----- |
PEM 格式的憑證 |
-----BEGIN RSA PRIVATE KEY----- |
PEM 格式的 RSA 私鑰 |
-----BEGIN PRIVATE KEY----- |
PEM 格式的私鑰(通用格式) |
-----BEGIN ENCRYPTED PRIVATE KEY----- |
有密碼保護的私鑰 |
-----BEGIN CERTIFICATE REQUEST----- |
CSR 憑證簽署請求 |
-----BEGIN PKCS7----- |
PKCS#7 格式 |
| 亂碼/二進位垃圾 | DER 或 PKCS#12(二進位格式) |
openssl 確認並解讀依據 Step 2 的判斷,用對應的指令:
# PEM 格式憑證
openssl x509 -in cert.pem -text -noout
# DER 格式憑證(二進位)
openssl x509 -in cert.der -inform DER -text -noout
# PKCS#12(.pfx / .p12)— 會問密碼
openssl pkcs12 -in cert.pfx -info -nokeys
# PKCS#7(.p7b)
openssl pkcs7 -in cert.p7b -print_certs -noout
# 私鑰
openssl rsa -in private.key -check -noout
# CSR
openssl req -in request.csr -text -noout
# 依序試,哪個成功就是哪種格式
openssl x509 -in mystery.file -text -noout 2>/dev/null && echo "==> PEM certificate"
openssl x509 -in mystery.file -inform DER -text -noout 2>/dev/null && echo "==> DER certificate"
openssl pkcs12 -in mystery.file -info -nokeys 2>/dev/null && echo "==> PKCS#12"
openssl pkcs7 -in mystery.file -print_certs 2>/dev/null && echo "==> PKCS#7"
openssl rsa -in mystery.file -check -noout 2>/dev/null && echo "==> RSA private key"
🏛️ Root CA(根憑證)
├── 自己簽自己(Self-Signed)
├── 預先安裝在 OS / 瀏覽器的信任庫裡
└── 簽發 ↓
🏢 Intermediate CA(中繼憑證)
├── 由 Root CA 簽發
├── 可能有多層(Intermediate 1 → Intermediate 2)
└── 簽發 ↓
📄 Leaf Certificate(葉憑證 / Server 憑證)
├── 由 Intermediate CA 簽發
└── 就是你的網站 / 服務用的那張憑證
Root CA 的私鑰太珍貴,不會直接拿來簽發終端憑證。用 Intermediate CA 當中間人:
openssl x509 -in cert.pem -text -noout | grep -E '(Subject:|Issuer:|CA:)'
| 特徵 | Leaf(Server) | Intermediate CA | Root CA |
|---|---|---|---|
| Subject ≠ Issuer | ✅ | ✅ | ❌(Subject = Issuer) |
CA:TRUE |
❌ | ✅ | ✅ |
CA:FALSE 或無此欄位 |
✅ | ❌ | ❌ |
| 可以簽發其他憑證 | ❌ | ✅ | ✅ |
# 看 Subject 和 Issuer
openssl x509 -in cert.pem -noout -subject -issuer
# 輸出範例(Leaf):
# subject= /CN=www.example.com
# issuer= /CN=Let's Encrypt Authority X3 ← 不同,是別人簽的
# 輸出範例(Root):
# subject= /CN=DST Root CA X3
# issuer= /CN=DST Root CA X3 ← 相同,自己簽自己
1. Leaf Certificate(你的 Server 憑證)
2. Intermediate CA 1(離 Leaf 最近的)
3. Intermediate CA 2(如果有多層)
4. Root CA(通常省略,因為 OS/瀏覽器已經有)
每張憑證的 Issuer 必須等於下一張憑證的 Subject:
# 假設你有三個檔案
for cert in leaf.pem intermediate.pem root.pem; do
echo "=== $cert ==="
openssl x509 -in "$cert" -noout -subject -issuer
echo ""
done
正確的輸出應該是一條完整的鏈:
=== leaf.pem ===
subject= /CN=www.example.com
issuer= /O=Let's Encrypt/CN=R3 ← 指向 intermediate
=== intermediate.pem ===
subject= /O=Let's Encrypt/CN=R3 ← 被 leaf 指向
issuer= /O=Digital Signature Trust/CN=DST Root CA X3 ← 指向 root
=== root.pem ===
subject= /O=Digital Signature Trust/CN=DST Root CA X3 ← 被 intermediate 指向
issuer= /O=Digital Signature Trust/CN=DST Root CA X3 ← 自己簽自己
確認順序正確後,直接串接:
cat leaf.pem intermediate.pem > fullchain.pem
# 如果需要包含 Root(少數情況)
cat leaf.pem intermediate.pem root.pem > fullchain-with-root.pem
⚠️ 注意:每個 PEM 區塊之間不能有空行缺失。確保每個
-----END CERTIFICATE-----後面接著下一個-----BEGIN CERTIFICATE-----(可以有一個換行)。
# 驗證 leaf 是否能透過 intermediate 追溯到 root
openssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem
# 驗證 fullchain(用系統信任庫)
openssl verify fullchain.pem
# 顯示完整鏈的細節
openssl verify -show_chain -CAfile root.pem -untrusted intermediate.pem leaf.pem
openssl x509 -in leaf.pem -text -noout | grep -A 3 "Authority Information Access"
輸出可能會有:
Authority Information Access:
CA Issuers - URI:http://r3.i.lencr.org/
OCSP - URI:http://r3.o.lencr.org
那個 CA Issuers 的 URL 就是 Intermediate CA 的下載位址:
# 通常下載下來是 DER 格式,需要轉換
curl -o intermediate.der http://r3.i.lencr.org/
openssl x509 -in intermediate.der -inform DER -out intermediate.pem -outform PEM
# 連到目標伺服器,把整條鏈抓下來
openssl s_client -connect example.com:443 -showcerts < /dev/null 2>/dev/null
輸出會包含所有憑證,每個 -----BEGIN CERTIFICATE----- 到 -----END CERTIFICATE----- 就是一張。
各大 CA 都有公開的 Intermediate / Root 憑證下載頁面。用 Issuer 裡的組織名稱去搜尋即可。
| 從 | 到 | 指令 |
|---|---|---|
| PEM → DER | 二進位 | openssl x509 -in cert.pem -outform DER -out cert.der |
| DER → PEM | 文字 | openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem |
| PEM → PKCS#12 | 打包含私鑰 | openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.pfx -certfile ca.pem |
| PKCS#12 → PEM | 拆出所有內容 | openssl pkcs12 -in cert.pfx -out all.pem -nodes |
| PKCS#12 → 只取憑證 | 不要私鑰 | openssl pkcs12 -in cert.pfx -clcerts -nokeys -out cert.pem |
| PKCS#12 → 只取私鑰 | 不要憑證 | openssl pkcs12 -in cert.pfx -nocerts -nodes -out key.pem |
| PKCS#12 → 取 CA 鏈 | 只要 CA | openssl pkcs12 -in cert.pfx -cacerts -nokeys -out ca.pem |
| PKCS#7 → PEM | 展開 | openssl pkcs7 -in cert.p7b -print_certs -out certs.pem |
| PEM → PKCS#7 | 打包(無私鑰) | openssl crl2pkcs7 -nocrl -certfile cert.pem -out cert.p7b |
# === 拿到檔案的 SOP ===
# 1. 這是什麼格式?
file mystery.crt
head -3 mystery.crt
# 2. 讀出憑證資訊
openssl x509 -in cert.pem -text -noout
# 3. 看有效期限
openssl x509 -in cert.pem -noout -dates
# 4. 看 Subject 和 Issuer(判斷角色 + 鏈的順序)
openssl x509 -in cert.pem -noout -subject -issuer
# 5. 看是不是 CA
openssl x509 -in cert.pem -text -noout | grep "CA:"
# 6. 驗證私鑰和憑證是否匹配
openssl x509 -in cert.pem -noout -modulus | openssl md5
openssl rsa -in key.pem -noout -modulus | openssl md5
# 兩個 MD5 值一樣就是配對的
# 7. 驗證完整憑證鏈
openssl verify -CAfile ca-bundle.pem cert.pem
# 8. 從線上伺服器抓憑證鏈
openssl s_client -connect example.com:443 -showcerts < /dev/null
# 9. 檢查線上伺服器的憑證到期日
openssl s_client -connect example.com:443 < /dev/null 2>/dev/null | \
openssl x509 -noout -dates
.crt 可能是 PEM 也可能是 DER,永遠用 file + openssl 確認