# 第 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/)