3 PKI 與信任鏈:公鑰憑什麼可信
上一章結尾留了一個洞:驗章需要公鑰,但公鑰憑什麼可信?這一章把這個洞補起來—— 補的過程會經過憑證、憑證鏈、一路遞迴到一個多數人沒想過的終點。
先預測:你覺得「保護公鑰」是什麼意思?是把它藏好不讓人看到嗎?
3.1 起點問題:公鑰要怎麼「保護」?
這個問題本身就踩在一個經典陷阱上——公鑰不需要保密,它本來就公開。 「保護」要拆成兩把鑰匙、兩種完全不同的威脅:
| 鑰匙 | 要防什麼 | 怎麼做 |
|---|---|---|
| 私鑰(拿來簽的那把) | 被偷(外洩=別人能冒充你簽) | HSM(Hardware Security Module,硬體安全模組)/keystore,私鑰不落地、不出晶片 |
| 公鑰 | 被掉包(中間人換成他的公鑰) | 憑證+信任鏈(本章主題) |
公鑰的威脅不是被偷——偷了也沒用,它本來就是要給全世界的。公鑰怕的是 被中間人(MITM,man-in-the-middle)換掉:攻擊者把「他自己的公鑰」冒充成 「某公司的公鑰」塞給你。從此他用自己的私鑰簽的所有東西,你都會驗成「某公司簽的」。 注意這個攻擊的陰險之處:你的驗章程式碼完全正確、每一步都回傳成功—— 錯的是你手上的鑰匙。
解決這個掉包問題,就是整個 PKI(Public Key Infrastructure,公鑰基礎建設) 存在的理由。
3.2 憑證:一段被 CA 簽過名的聲明
憑證(certificate)這個詞聽起來像某種神秘的技術物件,其實拆開只是:
「我 CA 保證:這把公鑰屬於某公司/某網域」+ CA 的簽章
一張 X.509 憑證(RFC 5280 (Cooper 等 2008年) 定義的標準格式)裡有:對方身分、對方公鑰、有效期、 CA 的簽章。(網域名稱只是「身分」欄位的一種寫法,不是憑證的本體。)
你憑什麼信這張憑證?先預測——是因為「對方持有對應的私鑰」嗎?
不是。你是因為信那家 CA(Certificate Authority,憑證授權中心)。驗證的動作是: 用 CA 的公鑰去驗「憑證上那個簽章」。驗過=這張憑證沒被竄改、確實是 CA 發的、 CA 確實擔保過「這把公鑰屬於這個身分」。
看到了嗎?這就是上一章的數位簽章,一個零件都沒換——私鑰簽、公鑰驗—— 只是這次簽名的人是 CA,被簽的內容是「某把公鑰屬於某人」這句話。 PKI 沒有發明新原語,它是把簽章遞迴地套在「分發公鑰」這個問題本身上。
3.3 憑證鏈與遞迴的終點
但你應該立刻聞到不對勁:驗憑證要用「CA 的公鑰」——那 CA 的公鑰又憑什麼沒被掉包? 用另一張憑證保證?那張憑證又用誰的公鑰驗?
這個遞迴是真實存在的,它的具體形狀叫憑證鏈(chain of trust):
你的憑證(leaf) → 中介憑證(intermediate) → 根憑證(root)
每一層都是「上一層用私鑰簽下一層」,驗證時一路往上驗簽章。那鏈憑什麼停下來?
- 根憑證(root)是 CA 自己簽自己(self-signed,自簽)——上面沒有更高的 CA 了。
- 但注意:「自簽」本身完全不提供安全性。我現在也能自簽一張「我是 Root CA」, 一秒鐘的事。self-signed ≠ 可信。
- 那你憑什麼信 DigiCert、Let’s Encrypt 那些 root?👉 因為它們被「預先放進」你的信任庫了 (OS/瀏覽器/Java 的
cacerts)。Microsoft、Apple、Mozilla、Oracle 對這些 CA 做過稽核, 然後把它們的 root 憑證出廠就塞進系統。
整條鏈的終點不是「數學」,是一個人為的信任決定(trust anchor,信任錨點)。 遞迴之所以停得下來,是因為鏈的底部踩在「出廠預裝、你選擇無條件相信」的東西上。
這正是前言那句主線的第一次完整現形:信任不是被算出來的,是被授予的。 密碼學把「Mozilla 決定信這家 CA」這個人為決定,沿著憑證鏈用數學傳遞到你面前—— 可驗證、防竄改——但決定本身,是人下的。
這個結論在供應鏈上的含義很實際:當有人跟你說「這個 artifact 有簽章、驗過了」, 你的下一個問題永遠應該是——「驗到哪個錨點?那個錨點是誰、憑什麼放進來的?」 簽章驗證成功只代表「鏈是完整的」,不代表「鏈的根是對的」。
3.4 Java 對照
Java 世界把「兩把鑰匙、兩種威脅」直接做成了兩個容器,對得整整齊齊:
- keystore:放你自己的私鑰+憑證——拿來「簽/證明自己」。對應上表第一列:防偷。
- truststore(
$JAVA_HOME/lib/security/cacerts):放你信任的 root CA 憑證—— 拿來「驗別人」。JDK 出廠就內建一堆 root,這就是 Java 世界的信任錨點。對應上表第二列:防掉包。
下次改 -Djavax.net.ssl.trustStore 或往 cacerts 裡 import 憑證時,記得你在做的事的真正意義: 你正在修改你的信任錨點集合——你在「授予」信任,而不是在做一個純技術設定。
3.5 現代做法:Sigstore(keyless)
傳統 PKI 的痛點在營運:長期私鑰要自管(HSM、輪替、保管人)、憑證要申請發放。 對「幫每次 build 簽章」這種高頻場景,管理成本很重。Sigstore 換了個思路:
- Fulcio:一個 CA,你用 OIDC/SSO 登入後,它發一張約 10 分鐘的短命憑證, 綁定你的身分+當場生成的臨時金鑰。簽完即丟,沒有長期私鑰可偷、可管。
- Rekor:透明日誌(transparency log),把每次簽章事件記帳存證,事後可稽核。
- 信任錨點變成=你組織已經在信的身分系統(SSO)。
注意 Sigstore 沒有推翻本章任何一個結論——它只是把「信任錨點」從 「出廠預裝的 root 憑證」搬到「組織的身分供應商」。信任仍然是被授予的, 只是授予的形式從「裝機時塞憑證」變成「SSO 上開帳號」。換湯,而湯碗的形狀值得你認得。
本章帶走的東西:私鑰怕偷、公鑰怕掉包,兩種威脅兩種解法;憑證=CA 對「公鑰屬於誰」 的簽章聲明,PKI 是簽章的遞迴套用;憑證鏈的終點是 trust anchor——人為決定,不是數學; self-signed ≠ 可信;驗章成功後永遠要追問「錨點是誰」。
往下一章的橋:到目前為止,簽章(驗身分)和加密(保密)一直分開談。實務上常常兩個都要—— 既要別人讀不到,又要能證明是誰發的。直覺告訴你「那就都做啊,先簽再加密,或先加密再簽」。 下一章會讓你看到:這兩種直覺順序,都有洞。這章比較深入,趕路的讀者可以先跳到第 5 章, 不影響主線。