# 第 10 篇:X.509 憑證格式 - 數位世界的身份證
## 前言:憑證到底是什麼?
還記得我們在系列二學到的「信任鏈」嗎?當時我們說:裝置有一組公私鑰對,但伺服器怎麼知道這個公鑰真的屬於這個裝置?答案就是:**數位憑證**。
在第 6 篇,我們看到 CBOR 可以包裝資料,而在實際應用中(例如 App Attest),CBOR 裡面會有個欄位叫 `x5c`,裡面裝的就是「憑證鏈」。
這些憑證用 **X.509 格式**儲存,它實現了我們之前學過的所有概念:
- 用 **數位簽章**來保證真實性
- 實現了 **信任鏈**的架構
- 包含 **公鑰**和身份資訊
本文將用最簡單的方式解釋:憑證是什麼、為什麼看起來像亂碼、以及如何理解憑證的結構。
## 從現實世界理解:身份證的設計
### 🪪 實體身份證的結構
想想你的身份證,上面有什麼資訊?
```
身份證
├── 照片(證明是你本人)
├── 姓名
├── 身份證字號
├── 出生日期
├── 發證日期
├── 有效期限
└── 發證機關的印章
```
**為什麼需要這些資訊?**
- ✅ 照片:確認是你本人
- ✅ 姓名、字號:識別你是誰
- ✅ 有效期限:過期了就要換新的
- ✅ 發證機關印章:證明這張身份證是真的
### 💳 數位憑證的結構
X.509 數位憑證就像數位世界的身份證:
```
X.509 憑證
├── 公鑰(相當於照片)
├── 持有者資訊(誰的憑證)
├── 序號(憑證編號)
├── 有效期限(什麼時候過期)
├── 發行者資訊(誰發的憑證)
└── 數位簽章(CA 的印章)
```
**核心概念:**
- 📸 **公鑰** = 你的照片(證明身份的關鍵)
- 📝 **持有者資訊** = 你的姓名(這個憑證屬於誰)
- 🏢 **發行者** = 發證機關(誰發的)
- ✍️ **數位簽章** = 政府印章(證明憑證真實性)
## X.509 憑證的三層結構
### 📦 憑證的外層包裝
X.509 憑證分成三個主要部分:
```
X.509 Certificate
│
├── tbsCertificate(憑證本體 - To Be Signed)
│ └── 所有要被簽章的內容都在這裡
│
├── signatureAlgorithm(簽章演算法)
│ └── 說明用什麼演算法簽章
│
└── signatureValue(簽章值)
└── CA 用私鑰產生的數位簽章
```
**為什麼這樣設計?**
```
類比:郵寄重要文件
📄 文件本體(tbsCertificate)
├── 包含所有重要資訊
└── 這是要被保護的內容
🖊️ 簽名方式說明(signatureAlgorithm)
└── 「我用鋼筆簽的」vs「我用印章蓋的」
✍️ 實際簽名(signatureValue)
└── 你的親筆簽名或印章
```
**驗證時的流程:**
1. 取出憑證本體(tbsCertificate)
2. 計算它的 hash 值
3. 用 CA 的公鑰驗證簽章值(signatureValue)
4. 如果驗證通過 → 憑證內容沒有被竄改 ✅
## 憑證本體(tbsCertificate)的欄位
### 📋 完整的欄位清單
```
tbsCertificate
├── version(版本)
├── serialNumber(序號)
├── signature(簽章演算法)
├── issuer(發行者)
├── validity(有效期限)
├── subject(持有者)
├── subjectPublicKeyInfo(公鑰資訊)
└── extensions(擴展欄位)- v3 才有
```
讓我們逐一理解每個欄位的作用:
### 1. Version(版本)
```
version: 3 種版本
v1 (0) - 1988 年
├── 最基本的欄位
└── 已過時
v2 (1) - 1993 年
├── 增加唯一識別碼欄位
└── 很少使用
v3 (2) - 1996 年 ⭐ 現在都用這個
├── 支援 Extensions(擴展欄位)
└── 功能最完整
```
**為什麼重要?**
- Extensions 讓憑證可以包含額外資訊(例如:用途限制、App ID 等)
- 現代系統基本上都用 v3
### 2. Serial Number(序號)
```
serialNumber: 每張憑證的唯一編號
例如:
├── 0x1a2b3c4d5e6f
├── 0x1234567890abcdef
└── 由 CA 分配,確保唯一
```
**用途:**
- 識別特定的憑證
- 撤銷憑證時用序號指定
- 類似身份證字號
### 3. Signature(簽章演算法)
```
signature: 說明用什麼演算法簽章
常見演算法:
├── ecdsa-with-SHA256(橢圓曲線 + SHA-256)
├── sha256WithRSAEncryption(RSA + SHA-256)
└── sha384WithRSAEncryption(RSA + SHA-384)
```
**為什麼需要?**
- 驗證簽章時需要知道用什麼演算法
- 不同演算法有不同的驗證流程
### 4. Issuer(發行者)
```
issuer: 誰簽發這張憑證
格式:Distinguished Name (DN)
├── CN (Common Name): 主要名稱
├── O (Organization): 組織
├── OU (Organizational Unit): 部門
├── C (Country): 國家
└── 其他欄位...
範例:
CN=Apple App Attestation CA 1
O=Apple Inc.
C=US
```
**為什麼重要?**
- 建立信任鏈的關鍵
- 驗證時需要找到對應的 CA 憑證
### 5. Validity(有效期限)
```
validity: 憑證的有效時間範圍
├── notBefore: 開始時間
└── notAfter: 結束時間
範例:
notBefore: 2024-01-15 00:00:00 UTC
notAfter: 2025-01-15 23:59:59 UTC
```
**為什麼需要有效期限?**
```
假設憑證永久有效:
1. 私鑰洩漏了
2. 駭客可以永遠使用這個憑證
3. 無法有效撤銷
有了有效期限:
1. 定期更換憑證
2. 即使洩漏,影響時間有限
3. 過期自動失效
```
### 6. Subject(持有者)
```
subject: 這張憑證屬於誰
格式:Distinguished Name (DN)(同 Issuer)
範例:
一般網站憑證:
CN=www.google.com
O=Google LLC
C=US
App Attest 憑證:
CN=6d2ac4845f13...(Key ID)
```
**與 Issuer 的關係:**
```
憑證鏈範例:
根憑證:
subject: CN=Apple Root CA
issuer: CN=Apple Root CA(自己簽自己)
中繼憑證:
subject: CN=Apple App Attestation CA 1
issuer: CN=Apple Root CA
葉憑證:
subject: CN=6d2ac4845f13...
issuer: CN=Apple App Attestation CA 1
```
**驗證邏輯:**
- 葉憑證的 `issuer` 應該等於中繼憑證的 `subject`
- 中繼憑證的 `issuer` 應該等於根憑證的 `subject`
### 7. Subject Public Key Info(公鑰資訊 SPKI)
```
subjectPublicKeyInfo: 憑證的核心價值
├── algorithm(演算法)
│ ├── 演算法類型(EC / RSA)
│ └── 參數(P-256 / P-384 等)
└── subjectPublicKey(公鑰數據)
└── 實際的公鑰 bits
```
**這是憑證存在的核心理由!**
```
憑證的目的就是:
「證明這個公鑰是真的」
流程:
1. 裝置產生金鑰對
2. 把公鑰給 CA
3. CA 驗證後,把公鑰包在憑證裡
4. CA 用自己的私鑰簽章整張憑證
5. 任何人都可以:
├── 看到公鑰(公開的)
├── 驗證 CA 簽章(確認憑證真實性)
└── 信任這個公鑰
```
**公鑰資訊範例:**
```
algorithm: id-ecPublicKey
parameters: prime256v1 (P-256)
publicKey:
04 a1 b2 c3 d4 e5 f6 ...(65 bytes)
└── 04 表示未壓縮格式
└── 接下來是 X 和 Y 座標各 32 bytes
```
### 8. Extensions(擴展欄位)
```
extensions: v3 才有,提供額外功能
標準擴展:
├── basicConstraints: 是否為 CA 憑證
├── keyUsage: 金鑰用途
├── extKeyUsage: 延伸用途
├── subjectAltName: 替代名稱
└── ...
自訂擴展:
└── 各廠商可定義專屬 OID
```
#### 常見擴展說明
**Basic Constraints(基本限制)**
```
用途:標示這是否為 CA 憑證
範例:
├── cA: TRUE → 這是 CA 憑證,可以簽發其他憑證
└── cA: FALSE → 這是終端憑證,不能簽發憑證
```
**Key Usage(金鑰用途)**
```
用途:限制公鑰可以做什麼
範例:
├── digitalSignature: 可用於數位簽章
├── keyEncipherment: 可用於金鑰加密
├── keyCertSign: 可用於簽發憑證
└── cRLSign: 可用於簽發撤銷清單
```
**Extended Key Usage(延伸金鑰用途)**
```
用途:更具體的用途限制
範例:
├── serverAuth: TLS 伺服器認證
├── clientAuth: TLS 客戶端認證
├── codeSigning: 程式碼簽章
└── emailProtection: 電子郵件保護
```
## 憑證鏈在 X.509 中的實現
在系列二第4篇,我們學過信任鏈的概念:**根 CA → 中繼 CA → 終端憑證**。現在我們來看這個概念如何透過 X.509 憑證的欄位實現。
### 🔗 憑證鏈的欄位關係
憑證鏈的關鍵在於 `subject` 和 `issuer` 欄位的配對:
```
根憑證:
subject: CN=Apple Root CA
issuer: CN=Apple Root CA ← 自己簽自己
basicConstraints: cA=TRUE ← 可以簽發其他憑證
中繼憑證:
subject: CN=Apple App Attestation CA 1
issuer: CN=Apple Root CA ← 由根憑證簽發
basicConstraints: cA=TRUE ← 可以簽發憑證
葉憑證:
subject: CN=6d2ac4845f13...
issuer: CN=Apple App Attestation CA 1 ← 由中繼憑證簽發
basicConstraints: cA=FALSE ← 不能簽發憑證
```
### 🔍 如何驗證憑證鏈?
利用 X.509 欄位進行驗證:
**步驟 1:配對 subject 和 issuer**
```
葉憑證的 issuer == 中繼憑證的 subject ✅
中繼憑證的 issuer == 根憑證的 subject ✅
```
**步驟 2:驗證數位簽章**
```
用中繼憑證的 publicKey 驗證葉憑證的 signature ✅
用根憑證的 publicKey 驗證中繼憑證的 signature ✅
```
**步驟 3:檢查 basicConstraints**
```
中繼憑證的 cA=TRUE(可以簽發憑證)✅
葉憑證的 cA=FALSE(不能簽發憑證)✅
```
**步驟 4:檢查有效期限**
```
所有憑證的 validity 都在有效範圍內 ✅
```
### 📋 實際範例
當你收到一個憑證鏈 `[cert0, cert1]`:
```python
# 1. 檢查鏈的連續性
assert cert0.issuer == cert1.subject
assert cert1.issuer == trusted_root.subject
# 2. 驗證簽章
verify_signature(cert0, cert1.public_key) # 用上一層的公鑰驗證
verify_signature(cert1, trusted_root.public_key)
# 3. 檢查約束
assert cert1.extensions.basicConstraints.cA == True
assert cert0.extensions.basicConstraints.cA == False
# 4. 檢查有效期
assert cert0.not_before <= now <= cert0.not_after
assert cert1.not_before <= now <= cert1.not_after
```
> 💡 **關於信任鏈的概念**:為什麼需要這樣的結構?如何建立信任?請參考系列二第4篇《公開金鑰與信任鏈概念》。
## 實務操作:查看憑證內容
### 💻 使用 OpenSSL
```bash
# 查看 PEM 格式憑證
openssl x509 -in certificate.pem -text -noout
# 查看 DER 格式憑證
openssl x509 -in certificate.der -inform DER -text -noout
# 輸出範例:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1234567890
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN=Apple App Attestation CA
Validity
Not Before: Jan 15 00:00:00 2024 GMT
Not After : Jan 15 23:59:59 2025 GMT
Subject: CN=Apple App Attestation
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub: 04:a1:b2:c3...
```
### 🌐 線上工具
```
推薦工具:
├── https://lapo.it/asn1js/
│ └── 視覺化 ASN.1 結構
├── https://certlogik.com/decoder/
│ └── 線上憑證解析器
└── https://www.sslshopper.com/certificate-decoder.html
└── 快速查看憑證資訊
```
### 📚 使用程式庫
**Python:**
```python
from cryptography import x509
from cryptography.hazmat.backends import default_backend
# 讀取 DER 格式
with open('cert.der', 'rb') as f:
cert_data = f.read()
cert = x509.load_der_x509_certificate(cert_data, default_backend())
# 查看資訊
print(f"Subject: {cert.subject}")
print(f"Issuer: {cert.issuer}")
print(f"Not Before: {cert.not_valid_before}")
print(f"Not After: {cert.not_valid_after}")
```
**PHP:**
```php
// PHP 原生支援
$certData = file_get_contents('cert.der');
$parsed = openssl_x509_parse($certData);
echo "Subject: " . $parsed['subject']['CN'] . "\n";
echo "Issuer: " . $parsed['issuer']['CN'] . "\n";
```
## 本文小結
✅ **X.509 是憑證格式的標準**:定義憑證應該包含哪些欄位
✅ **憑證 = 公鑰 + 身份 + CA 簽章**:證明公鑰的真實性
✅ **ASN.1 DER 編碼**:憑證的二進位包裝格式
✅ **憑證鏈**:根 → 中繼 → 葉,層層建立信任
✅ **實務工具**:OpenSSL / 線上工具 / 程式庫
### 🎓 與前面文章的連結
**系列二《數位簽章》→ 憑證的核心技術**
- 每張憑證都有 CA 的數位簽章
- 實現身份驗證和完整性保護
**系列二第7篇《信任鏈》→ 憑證鏈的具體實現**
- 根憑證 → 中繼憑證 → 葉憑證
- 這就是信任鏈的實際應用
**第9篇《CBOR》→ 憑證的傳輸方式**
- 憑證用 DER 編碼
- 裝在 CBOR 的 `x5c` 欄位傳輸
### 📊 重點整理
| 項目 | 說明 |
|------|------|
| **憑證格式** | X.509 |
| **編碼方式** | ASN.1 DER |
| **核心價值** | 證明公鑰的真實性 |
| **信任來源** | CA 數位簽章 |
| **信任鏈** | 根 → 中繼 → 葉 |
## 為什麼需要理解 ASN.1?
到這裡,我們已經理解了 X.509 憑證的邏輯結構:包含哪些欄位、每個欄位的作用、憑證鏈如何運作。
**但還有一個問題:**
當你實際查看憑證檔案時,看到的是這樣:
```
30 82 02 cc 30 82 02 34 a0 03 02 01 02 02 10 1a 2b 3c 4d...
```
而不是容易閱讀的結構。這就是因為憑證使用 **ASN.1 DER 編碼**儲存。
**為什麼要用這種「亂碼」格式?**
- 跨平台標準:不同系統都能解讀
- 確保唯一性:同樣內容只有一種編碼方式
- 數位簽章需要:hash 計算結果必須一致
**你需要理解編碼細節嗎?**
大部分情況下不需要!使用工具就能查看和處理憑證。但如果你想:
- 理解憑證為什麼是二進位格式
- 知道 DER、BER、PEM 的差異
- 深入實作憑證解析
那麼下一篇《第11篇:ASN.1 與編碼格式》會詳細解釋這些內容。
## 下一篇預告
第11篇《ASN.1 與編碼格式》將解答:
- 為什麼憑證看起來像亂碼?
- ASN.1 是如何定義資料結構的?
- DER、BER、PEM 有什麼差異?
- 如何在不同格式間轉換?
**學習建議:**
- 如果你只想「使用」憑證 → 可以跳過第8篇,直接看後續應用篇
- 如果你想「理解底層」→ 建議閱讀第8篇
---
## 💡 補充資料
### 常見問題
**Q: 我一定要深入理解 ASN.1 嗎?**
A: 不用!使用現成函式庫就可以了。理解概念比理解編碼細節更重要。
**Q: 憑證和公鑰有什麼不同?**
A: 公鑰只是一串數字,憑證是「公鑰 + 身份 + CA 簽章」的完整包裝。
**Q: 為什麼憑證會過期?**
A: 安全考量。限制私鑰洩漏的影響範圍,強制定期更新。
**Q: PEM 和 DER 有什麼不同?**
A:
- **DER**:純二進位(`30 82 02 cc...`)
- **PEM**:DER 的 Base64 + 標頭(`-----BEGIN CERTIFICATE-----`)
### 實用資源
- [OpenSSL 文件](https://www.openssl.org/docs/)
- [RFC 5280 - X.509 規格](https://tools.ietf.org/html/rfc5280)
- [Apple CA 憑證下載](https://www.apple.com/certificateauthority/)
- [線上 ASN.1 解析器](https://lapo.it/asn1js/)