在前面的章節中,我們建立在傳統非對稱密碼學(RSA、ECC、ECDH、ECDSA)與 PKI 體系的基礎上,構築了現代網路安全(TLS 1.3)與硬體安全(HSM/PKCS#11)的堅固防線。這些密碼學機制的安全性,全都依賴於傳統電腦無法在合理時間內解答的困難數學問題:大數分解問題(Integer Factorization)與離散對數問題(Discrete Logarithm Problem, DLP / ECDLP)。
然而,這座建造了數十年的安全大廈,正面臨著來自未來技術的毀滅性挑戰——量子電腦(Quantum Computer)。
2024 年 8 月,美國國家標準與技術研究院(NIST)正式頒布了全球首批後量子密碼學(Post-Quantum Cryptography, PQC)官方標準(FIPS 203、FIPS 204 與 FIPS 205),標誌著密碼學正式邁入 PQC 轉型時代。本章將剖析量子電腦對現有密碼學的實質威脅、解析 NIST PQC 新標準演算法(ML-KEM 與 ML-DSA)的數學基石(格密碼學),並透過 Java 實作新一代抗量子加密與數位簽章。
量子電腦利用量子位元(Qubit)的「疊加(Superposition)」與「糾纏(Entanglement)」特性,能在特定數學問題上實現相較於傳統電腦的指數級加速(Exponential Speedup)。
[ 傳統密碼學數學難題 ]
├── 大數分解問題 (RSA) ──┐ (Shor 演算法: 指數級加速)
├── 離散對數問題 (Diffie-Hellman) ──┼─────────────────────────────> 🚨 完全破解 (多項式時間內解開)
└── 橢圓曲線離散對數問題 (ECC/ECDSA) ──┘
[ 對稱加密與雜湊函式 ]
├── AES-128 / AES-256 ──┐ (Grover 演算法: 平方級加速)
└── SHA-256 / SHA-3 ──┴─────────────────────────────> ⚠️ 安全強度減半 (AES-256 依然安全)
1994 年由 Peter Shor 提出的量子演算法,可以在多項式時間(Polynomial Time, $\mathcal{O}((\log N)^3)$)內解出大數分解與離散對數問題。
1996 年由 Lov Grover 提出的量子搜尋演算法,能對無結構的資料庫搜尋實現平方級加速(Quadratic Speedup)。
許多人認為:「量子電腦離商業化還有 10 到 15 年,我們現在需要擔心嗎?」
答案是:必須立刻採取行動。攻擊者(如國家級駭客組織)目前正在大量側錄並儲存金融機構、政府與企業傳送的 TLS 加密流量。一旦量子電腦問世,他們將拿著量子電腦解密歷史上錄下的所有敏感資料。這意味著,保護長效型敏感資料(如國防機密、醫療病歷、長期商業合約)的金鑰交換機制,必須現在就升級為 PQC!
經過長達 8 年(2016~2024)的全球密碼學徵集與多輪嚴格審查,NIST 於 2024 年 8 月正式發布了三項最終版 PQC 標準:
| 標準編號 | 官方演算法名稱 | 前身專案名稱 | 數學族群 | 密碼學用途 | 替代對象 |
|---|---|---|---|---|---|
| FIPS 203 | ML-KEM | CRYSTALS-Kyber | 模組格 (Module-LWE) | 金鑰封裝機制 (KEM) | RSA Key Exchange, ECDH |
| FIPS 204 | ML-DSA | CRYSTALS-Dilithium | 模組格 (Module-LWE) | 數位簽章 (Digital Signature) | RSA PKCS#1 v1.5/PSS, ECDSA |
| FIPS 205 | SLH-DSA | SPHINCS+ | 無狀態雜湊樹 (Hash-Based) | 數位簽章 (Digital Signature) | 保守備用簽章(不依賴格數學) |
ML-KEM 與 ML-DSA 的安全基礎建立在高維度格(Lattice)上的困難數學難題——例如容錯學習問題(Learning With Errors, LWE) 與 Module-LWE。
幾何學上,格是高維空間中無限延伸的散落點陣列。在幾何格空間中,尋找兩個點之間的最短距離向量,稱為最短向量問題(Shortest Vector Problem, SVP)。當空間維度擴展到數百甚至數千維時,不論是傳統電腦還是量子電腦,都無法## 四、 渡過期過渡架構:混合式金鑰交換 (Hybrid Key Exchange)
由於 PQC 演算法問世時間較短,密碼學界擔心未來格數學可能存在尚未被發現的理論漏洞。因此,RFC 9370 與 TLS 1.3 推進了混合式加密(Hybrid Cryptography):
$$
\text{Shared Secret} = \text{HKDF}(\text{ECDH Secret} \parallel \text{ML-KEM Secret})
$$
2 維空間的格 (簡單) 500+ 維度的高維格 (NP-Hard 難題)
• • • • • ┌ ┐
• • • • • ───(量子電腦無法解)───> │ 求解 Shortest Vector (SVP) │
• • • • • │ 找不到高維度格子點的極小向量 │
• • • • • └ ┘
在傳統 DH 中,兩端透過交換公鑰協商共享密鑰;而在 PQC 中,標準規範採用 KEM(金鑰封裝機制):
[ 接收方 (Bob) ] [ 發送方 (Alice) ]
│ │
├─ 1. 生成 (pk, sk) │
├────────────────────── 2. 傳送公鑰 pk ────────────────────>│
│ │
│ ├─ 3. [c, K] = Encaps(pk)
│ │ (產出密文 c 與對稱金鑰 K)
│<───────────────────── 4. 傳送封包密文 c ──────────────────┤
│ │
├─ 5. K = Decaps(sk, c) │
│ (解封算出相同的對稱金鑰 K) └─ 持有對稱金鑰 K
由於 PQC 演算法問世時間較短,密碼學界擔心未來格數學可能存在尚未被發現的理論漏洞。因此,RFC 9370 與 TLS 1.3 推進了混合式加密(Hybrid Cryptography):
$$
\text{Shared Secret} = \text{HKDF}(\text{ECDH Secret} \parallel \text{ML-KEM Secret})
$$
標準 JDK 目前正在透過 JEP 逐步將 PQC 整合至原生 JCA 中。目前生產環境中最完整支援 NIST FIPS 203/204 的標準庫是 Bouncy Castle (v1.78+)。
以下範例演示如何在 Java 中使用 Bouncy Castle 執行 ML-KEM-768 金鑰封裝與 ML-DSA-65 數位簽章:
import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.jcajce.spec.MLDSAParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.MLKEMParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.KEMExtractSpec;
import javax.crypto.spec.KEMGenerateSpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
public class PostQuantumCryptographyProductionExample {
public static void main(String[] args) throws Exception {
// 1. 動態註冊 Bouncy Castle PQC Provider
Security.addProvider(new BouncyCastlePQCProvider());
System.out.println("=== Day 37 - 後量子密碼學 (PQC) Java 實戰 ===");
// =========================================================================
// 實戰 1: ML-KEM-768 (FIPS 203) - 後量子金鑰封裝機制
// =========================================================================
System.out.println("\n--- [ 1. ML-KEM-768 金鑰封裝與解封測試 ] ---");
// Step 1.1: 接收方 (Bob) 產生 ML-KEM-768 金鑰對
KeyPairGenerator kemKeyPairGen = KeyPairGenerator.getInstance("ML-KEM", "BCPQC");
kemKeyPairGen.initialize(MLKEMParameterSpec.ml_kem_768, new SecureRandom());
KeyPair bobKeyPair = kemKeyPairGen.generateKeyPair();
System.out.println("Bob 公鑰長度 (ML-KEM-768) : " + bobKeyPair.getPublic().getEncoded().length + " bytes");
System.out.println("Bob 私鑰長度 (ML-KEM-768) : " + bobKeyPair.getPrivate().getEncoded().length + " bytes");
// Step 1.2: 發送方 (Alice) 使用 Bob 的公鑰封裝 (Encapsulate) 產出對稱金鑰與密文
KeyGenerator keyEncapsulator = KeyGenerator.getInstance("ML-KEM", "BCPQC");
keyEncapsulator.init(new KEMGenerateSpec(bobKeyPair.getPublic(), "AES"), new SecureRandom());
SecretKeyWithEncapsulation encapsResult = (SecretKeyWithEncapsulation) keyEncapsulator.generateKey();
byte[] ciphertext = encapsResult.getEncapsulation(); // 傳給 Bob 的封裝密文
SecretKey aliceSharedKey = encapsResult; // Alice 算出的 AES 金鑰
System.out.println("Alice 產出的密文長度: " + ciphertext.length + " bytes");
// Step 1.3: 接收方 (Bob) 使用自己的私鑰解封 (Decapsulate) 密文
KeyGenerator keyDecapsulator = KeyGenerator.getInstance("ML-KEM", "BCPQC");
keyDecapsulator.init(new KEMExtractSpec(bobKeyPair.getPrivate(), ciphertext, "AES"));
SecretKey bobSharedKey = (SecretKey) keyDecapsulator.generateKey();
// Step 1.4: 校驗兩端產出的對稱金鑰是否完全一致
boolean keyMatched = MessageDigest.isEqual(aliceSharedKey.getEncoded(), bobSharedKey.getEncoded());
System.out.println("ML-KEM-768 金鑰協商成功,兩端 AES 金鑰一致: " + keyMatched);
// =========================================================================
// 實戰 2: ML-DSA-65 (FIPS 204) - 後量子數位簽章
// =========================================================================
System.out.println("\n--- [ 2. ML-DSA-65 數位簽章與驗證測試 ] ---");
// Step 2.1: 產生 ML-DSA-65 簽章金鑰對
KeyPairGenerator dsaKeyPairGen = KeyPairGenerator.getInstance("ML-DSA", "BCPQC");
dsaKeyPairGen.initialize(MLDSAParameterSpec.ml_dsa_65, new SecureRandom());
KeyPair signerKeyPair = dsaKeyPairGen.generateKeyPair();
System.out.println("簽署者公鑰長度 (ML-DSA-65): " + signerKeyPair.getPublic().getEncoded().length + " bytes");
// Step 2.2: 執行簽章
byte[] dataToSign = "Post-Quantum Security Message 2026".getBytes(StandardCharsets.UTF_8);
Signature signer = Signature.getInstance("ML-DSA", "BCPQC");
signer.initSign(signerKeyPair.getPrivate());
signer.update(dataToSign);
byte[] signature = signer.sign();
System.out.println("ML-DSA-65 簽章結果長度 : " + signature.length + " bytes");
// Step 2.3: 執行簽章驗證
Signature verifier = Signature.getInstance("ML-DSA", "BCPQC");
verifier.initVerify(signerKeyPair.getPublic());
verifier.update(dataToSign);
boolean isSignatureValid = verifier.verify(signature);
System.out.println("ML-DSA-65 數位簽章驗證結果: " + isSignatureValid);
}
}