前言
PKI 基礎設施的量子威脅
公開金鑰基礎建設(Public Key Infrastructure, PKI)是現代網路安全的基石,支撐著:
- HTTPS 加密通訊(全球 95%+ 網站)
- 程式碼簽章(軟體發布、App Store、Docker 映像)
- 電子郵件加密(S/MIME)
- VPN 與企業身份認證(IPsec、802.1X)
- 物聯網裝置認證(mTLS、OTA 更新)
傳統 PKI 完全依賴 RSA 與 ECDSA 簽章演算法,這些演算法在量子電腦面前將完全失效:
| 演算法 | 目前安全性 | 量子電腦攻擊 | 破解時間估算 |
|---|---|---|---|
| RSA-2048 | 112-bit 安全 | Shor’s Algorithm | ~8 小時(大型量子電腦) |
| RSA-4096 | 140-bit 安全 | Shor’s Algorithm | ~1 天 |
| ECDSA P-256 | 128-bit 安全 | Shor’s Algorithm | ~數小時 |
| ECDSA P-384 | 192-bit 安全 | Shor’s Algorithm | ~1 天 |
危機時間點:
- 2030-2035 年:預估為 RSA/ECDSA 安全終結
- 現在(2026):必須立即開始遷移準備
- 遷移週期:企業 PKI 遷移通常需要 3-5 年
混合憑證的必要性
問題:如果今天全面切換到後量子憑證(Pure PQC),會發生什麼?
災難性後果:
- 舊版瀏覽器(Chrome 90 以前、Safari 14 以前)無法驗證
- 嵌入式裝置(路由器、IoT)韌體無法更新
- 舊系統(Windows 7、Android 8)完全失去連線能力
- 全球數億台裝置瞬間「斷網」
混合憑證解決方案:
混合憑證 = 傳統演算法(ECDSA P-256)+ 後量子演算法(ML-DSA-65)
驗證策略:
- 舊客戶端:只驗證 ECDSA → 可以連線(量子不安全)
- 新客戶端:雙重驗證 ECDSA + ML-DSA → 量子安全!
- 升級路徑:逐步淘汰舊客戶端,最終只要求 ML-DSA
平滑遷移時間表:
| 階段 | 時間 | CA 配置 | 客戶端要求 |
|---|---|---|---|
| 階段 1 | 2026-2028 | 混合憑證(ECDSA + ML-DSA) | 只驗證 ECDSA |
| 階段 2 | 2028-2030 | 混合憑證 | 新客戶端開始驗證雙重簽章 |
| 階段 3 | 2030-2032 | 混合憑證 | 強制雙重驗證(舊客戶端淘汰) |
| 階段 4 | 2032+ | Pure PQC(僅 ML-DSA) | 只驗證 ML-DSA |
瀏覽器與公開 PKI 現況(2026 年 9 月)
金鑰交換:已經是多數
混合後量子金鑰交換早已從實驗變成預設。Chrome 116(2023-08)先上 X25519Kyber768,Chrome 131(2024-11)換成標準版 X25519MLKEM768(FIPS 203)。到 2025 年 10 月,Cloudflare 網路上過半的人類發起流量已使用後量子金鑰協商,2026 年更超過六成(來源:Cloudflare 2025 報告、Cloudflare Radar)。OpenSSL 3.5 起也把 hybrid KEM 排進 TLS 預設群組,伺服器端幾乎不需額外設定。
效能代價很小:TLS 握手時間增加約 4%,server→client 多 1.1kB、client→server 多 1.2kB。
憑證簽章:仍卡在公開信任這一關
| 項目 | 狀態(2026-09) |
|---|---|
| ML-DSA 在 TLS 1.3 的簽章協商 | draft-ietf-tls-mldsa-05 已獲 IESG 核准(Informational),待出 RFC |
| 公開 TLS 憑證可否用 ML-DSA | CA/B Forum 尚無通過的 Baseline Requirements ballot |
| 主流瀏覽器預設 | 不協商 ML-DSA 伺服器憑證 |
| 替代路線 | Merkle Tree Certificates(draft-10)、TLS Trust Anchor Identifiers(draft-04)仍在草案階段 |
阻力仍然是尺寸。ML-DSA-44 的簽章 2,420 bytes、公鑰 1,312 bytes,對照 ECDSA P-256 的 ~64 / 65 bytes 差了兩個數量級;本文後面用 OpenSSL 實測的一張 ML-DSA-65 自簽根憑證,DER 就要 5,532 bytes。整條鏈換成 ML-DSA 遠超 TLS 握手的舒適區,Chrome 團隊因此把力氣放在 Merkle Tree Certificates —— 用不到 1kB 的 Merkle proof 取代多數簽章,而不是單純換演算法。
這對 NG-CA 的意義
公開 TLS 簽發在 2026 年仍不是選項。PQC CA 現階段的落點是私有 PKI、企業 mTLS、程式碼簽章與 S/MIME —— 這些場景的信任錨由組織自己控制,不必等 Root Program 投票。NG-CA 的準備是:
- 支援混合憑證(ECDSA P-256 + ML-DSA-65),在效能與安全之間取得平衡
- composite 與 non-composite 兩種模式並存,讓同一套 CA 能同時服務老裝置與新建基礎設施
- 演算法與格式跟著 IETF LAMPS 走,等公開信任的門打開時不必重做
實作邊界:應用層、HSM 與 OpenSSL 生態
NG-CA 的關鍵設計決策不在程式語言,而在信任邊界切在哪裡:
| 層次 | 由誰負責 | 理由 |
|---|---|---|
| 受 FIPS 規範的金鑰與密碼學運算 | Thales Luna HSM(FIPS 140-3 Level 3) | 私鑰不落地,認證邊界清楚 |
| 尚未進 HSM 的 PQC(SLH-DSA / FN-DSA) | 應用層軟體實作 | Luna 韌體未支援,先以軟體補齊演算法矩陣 |
| 憑證與 CRL 的 ASN.1 組裝、策略、稽核 | 應用層 | 不涉及密碼學模組認證範圍 |
| 對外互通驗證 | OpenSSL 3.5 / 4.0、Bouncy Castle | 用生態主力實作當裁判,而不是自我驗證 |
應用層以 Rust 撰寫(x509-cert 組 ASN.1、自有 luna-cryptoki crate 包 PKCS#11 v3.0 FFI),但這是工程偏好,不是架構主張 —— 同一套邊界用 Go、Java 或 C 都能實作。真正不可替換的只有兩件事:私鑰只存在於 HSM,以及憑證格式必須能被主流實作解讀。
值得澄清一個常見誤解:建 PQC CA 不需要「擺脫 OpenSSL」。OpenSSL 自 3.5 起已原生支援 ML-KEM、ML-DSA 與 SLH-DSA,是今天最重要的 PQC 互通對象;NG-CA 與它是互補關係,不是取代關係。後面的互通實測會說明這條互通線的界線落在哪裡。
金鑰素材的型別邊界
金鑰來源分成 HSM 與軟體兩種,用 enum 在型別系統強制區分:
pub enum KeyMaterial {
/// 軟體實作的真實私鑰位元組(SLH-DSA / FN-DSA)
Software(Vec<u8>),
/// HSM-backed 私鑰的 CKA_LABEL UTF-8 bytes;實際私鑰永不離開 HSM
HsmLabel(Vec<u8>),
}
RSA / ECC / ML-DSA / ML-KEM 的金鑰生命週期完全託管給 Luna HSM,應用層只持有 CKA_LABEL。SLH-DSA 與 FN-DSA 因 Luna 韌體尚未支援,仍走軟體實作(pqcrypto / RustCrypto)。這個區分若只用 Vec<u8> 表示,把 HSM label 誤當 PKCS#8 DER 解析會在執行期才爆炸。
NG-CA 架構設計
系統整體架構(六層 + Luna HSM Bridge)

整體系統分為六層,L5 HSM Bridge 是 2026 年導入的關鍵層,把 RSA / ECC / ML-DSA / ML-KEM 的金鑰生命週期完全託管給 Luna HSM,金鑰素材以前述 KeyMaterial enum 區分 HSM 與軟體兩種來源。
模組架構概要
核心模組(backend/src/):
- crypto/:密碼學核心層 — RSA / ECC / ML-DSA (PqcProvider) / SLH-DSA / FN-DSA / ML-KEM / Hybrid (兩種模式) + X.509 / CSR / Name Constraints
- hsm/:Luna HSM bridge —
luna-cryptoki包裝、connection pool、CKM_RSA_PKCS/CKM_ECDSA_SHA*/CKM_ML_DSA/CKM_ML_KEM_*mechanism 對映 - api/:RESTful API 層(CA 管理、憑證管理、verify-chain、錯誤處理)
- acme/:ACME v2 協議實作(RFC 8555,HTTP-01,JWS ES256/RS256 驗證)
- ocsp/:OCSP 實作(RFC 6960,含 Nonce 擴展、
next_update、moka cache) - validation/:Name Constraints、CSR Validator、台灣身份檢核
- metrics/:Prometheus exporter(25+ 指標)
- db/:SQLx (SQLite) —
ng-ca.db業務資料、audit.dbSOC2 稽核 - audit/:稽核日誌寫入(actor / action / resource / status / metadata)
前端技術架構
Vue 3 + TypeScript 實作:
- 頁面組件:Dashboard(儀表板)、CaList(CA 管理)、CertList(憑證管理)
- 功能組件:CreateCaDialog(CA 建立)、IssueCertDialog(憑證簽發)、CertDetailDialog(憑證詳情)
- 狀態管理:Pinia Store(CA 與憑證資料)
- 路由管理:Vue Router(頁面導航)
- API 層:Axios 客戶端(RESTful API 呼叫)
- 工具函數:日期格式化、表單驗證
資料庫架構
業務資料庫 (ng-ca.db) - CA 與憑證資料:
| 資料表 | 關鍵欄位 | 說明 |
|---|---|---|
| cas | id, name, algorithm | CA 實體,支援 RSA/ECC/ML-DSA/Hybrid 演算法 |
| public_key_pem, pqc_public_key_pem | 傳統與 PQC 公鑰(混合模式雙公鑰) | |
| private_key_bytes, pqc_private_key_bytes | 加密儲存的私鑰資料 | |
| parent_ca_id, is_ca, path_len | CA 階層架構(Root/Intermediate) | |
| default_days, default_key_usage | RA 策略配置 | |
| certificates | serial_number, subject, issuer_id | 已簽發憑證索引 |
| pem, status, not_before, not_after | 憑證內容與狀態(Good/Revoked) | |
| revoked_at | 撤銷時間戳記 |
索引策略:idx_cert_status(狀態查詢)、idx_cert_issuer(CA 關聯)、idx_cert_subject(主體查詢)
稽核資料庫 (audit.db) - SOC2 合規日誌:
| 資料表 | 關鍵欄位 | 說明 |
|---|---|---|
| audit_logs | id, timestamp, actor_ip | 操作時間與來源 IP |
| action, resource_type, resource_id | 操作類型(create_ca/issue_cert/revoke_cert)與目標資源 | |
| status, metadata | 操作結果與額外資訊(JSON) |
索引策略:idx_audit_timestamp(時間範圍查詢)、idx_audit_action(操作類型統計)
PQC 整合實現
標準狀態一覽(2026 年 9 月)
PQC PKI 的標準地景在 2025–2026 年變動很大,幾份 NG-CA 原本引用的草案已經出 RFC,也有一份至今沒動。撰稿時的實況:
| 標準 | 狀態(2026-09) | 對 PQC CA 的意義 |
|---|---|---|
| FIPS 203 / 204 / 205 | 已定案(2024-08) | ML-KEM / ML-DSA / SLH-DSA 的演算法基準 |
| FIPS 206(FN-DSA) | 仍標示 in development,NIST 連 initial public draft 都尚未公開 | FN-DSA 只能當實驗性支援,不可作為合規宣稱 |
| RFC 9881 — ML-DSA in X.509 | Proposed Standard | ML-DSA 憑證的 OID 與 SPKI 編碼依據 |
| RFC 9909 — SLH-DSA in X.509 | Proposed Standard | SLH-DSA 憑證編碼依據 |
| RFC 9935 — ML-KEM in X.509(2026-03) | Proposed Standard | 取代原先引用的 draft-ietf-lamps-kyber-certificates |
| RFC 9882 — ML-DSA in CMS | Proposed Standard | 程式碼簽章 / S/MIME 路線的依據 |
| RFC 9794 — PQ/T hybrid 術語 | Informational(2025-06) | non-composite 與 composite 的用語來源 |
draft-ietf-lamps-pq-composite-sigs-19 | 已通過 IESG,在 RFC Editor queue(First Edit),尚未取號 | composite 模式的對齊版本;技術內容已凍結 |
draft-ietf-lamps-pq-composite-kem-21 | 仍在 WG(2026-09 更新) | composite KEM 尚未穩定,NG-CA 不實作 |
| ITU-T X.509 (2019) §9.8 alt 擴充 | 已生效的 ITU-T 標準 | non-composite 三個擴充(2.5.29.72/73/74)的唯一規範來源 |
draft-truskovsky-lamps-pq-hybrid-x509 | 已終止,IETF 不再推進 | 引用 alt 擴充時不應再指向 IETF 草案 |
| CA/B Forum TLS BR | 尚無允許 ML-DSA 的 ballot 通過 | 公開 TLS 的 PQC 簽發仍未開放 |
兩點值得特別記住:
- composite 的技術內容已凍結(draft-19 在 RFC Editor 手上),現在對齊它不會再被大改;但它與早期草案差異很大,見下文的編碼說明。
- FIPS 206 的進度比多數人以為的慢。NIST 於 2025 年 8 月把草案送審,但到 2026 年 9 月,CSRC 上仍沒有 FIPS 206 的出版頁面。任何把 FN-DSA 寫進合規文件的計畫都該假設它還會再等一年。
17 個演算法、5 大類別總覽
NG-CA 的演算法支援對齊 FIPS 203/204/205,以及 RFC 9794/9881/9909/9935 等 X.509 編碼規範。FN-DSA 所依據的 FIPS 206 至今仍未定案,實作細節見標準狀態一覽:
| 類別 | 演算法 | NIST 等級 | Backend | OID | 簽章 / 公鑰大小 |
|---|---|---|---|---|---|
| Traditional FIPS 186-5 | RSA-2048 | — | Luna HSM | 1.2.840.113549.1.1.11 | 256 B sig |
| RSA-3072 | — | Luna HSM | 同上 | 384 B sig | |
| RSA-4096 | — | Luna HSM | 同上 | 512 B sig | |
| ECDSA P-256 | — | Luna HSM | 1.2.840.10045.3.1.7 | ~64 B sig | |
| ECDSA P-384 | — | Luna HSM | 1.3.132.0.34 | ~96 B sig | |
| ECDSA P-521 | — | Luna HSM | 1.3.132.0.35 | ~132 B sig | |
| PQ 簽章 · lattice FIPS 204 / RFC 9881 | ML-DSA-44 | Level 2 | Luna HSM | 2.16.840.1.101.3.4.3.17 | 2,420 B sig |
| ML-DSA-65 | Level 3 | Luna HSM | 2.16.840.1.101.3.4.3.18 | 3,309 B sig | |
| ML-DSA-87 | Level 5 | Luna HSM | 2.16.840.1.101.3.4.3.19 | 4,627 B sig | |
| PQ 簽章 · hash-based FIPS 205 / RFC 9909 | SLH-DSA-SHA2-128s | Level 1 | 軟體 | 2.16.840.1.101.3.4.3.20 | 7,856 B sig |
| SLH-DSA-SHA2-192s | Level 3 | 軟體 | 2.16.840.1.101.3.4.3.22 | 16,224 B sig | |
| SLH-DSA-SHA2-256s | Level 5 | 軟體 | 2.16.840.1.101.3.4.3.24 | 29,792 B sig | |
| PQ 簽章 · FALCON FIPS 206 尚在制定 | FN-DSA-512 | Level 1 | 軟體 | 待 IETF 登記 | ~666 B sig |
| FN-DSA-1024 | Level 5 | 軟體 | 待 IETF 登記 | ~1,280 B sig | |
| PQ KEM FIPS 203 / RFC 9935 | ML-KEM-512 | Level 1 | Luna HSM | 2.16.840.1.101.3.4.4.1 | 800 B pk |
| ML-KEM-768 | Level 3 | Luna HSM | 2.16.840.1.101.3.4.4.2 | 1,184 B pk | |
| ML-KEM-1024 | Level 5 | Luna HSM | 2.16.840.1.101.3.4.4.3 | 1,568 B pk |
ECDSA 欄位列的是曲線 OID(prime256v1 等),簽章演算法 OID 另有 ecdsa-with-SHA* 一組。RSA 三種金鑰長度共用 sha256WithRSAEncryption,實際可搭配的 hash 見後文「RSA Hash 演算法擴展」。
在這 17 個演算法之上,Hybrid 與 HybridDualSignature 兩種混合模式各自把一組傳統金鑰與一組 ML-DSA 金鑰綁在同一張憑證上,不算獨立演算法。
Backend 邊界規則:
- Luna HSM 7.8+ 已支援 RSA、ECC、ML-DSA、ML-KEM 的硬體 keygen 與簽章/encap,所有此類金鑰皆走 HSM。
- SLH-DSA / FN-DSA Luna 尚未支援,落到軟體實作(
pqcrypto-sphincsplus/pqcrypto-falcon)。 - ML-KEM 為 KEM 演算法,KeyUsage 只能是
keyEncipherment,不可用於數位簽章;NG-CA 在 keygen 與用途檢查時都會拒絕誤用。
ML-DSA 詳解(最常用的 PQC 簽章)
FIPS 204 / RFC 9881
| 模式 | NIST 等級 | 公鑰 | 私鑰 | 簽章 | OID |
|---|---|---|---|---|---|
| ML-DSA-44 (原 Dilithium2) | Level 2 | 1,312 B | 2,544 B | 2,420 B | 2.16.840.1.101.3.4.3.17 |
| ML-DSA-65 (原 Dilithium3) | Level 3 | 1,952 B | 4,000 B | 3,309 B | 2.16.840.1.101.3.4.3.18 |
| ML-DSA-87 (原 Dilithium5) | Level 5 | 2,592 B | 5,632 B | 4,627 B | 2.16.840.1.101.3.4.3.19 |
HSM-backed 流程(Luna 7.8+):
- 金鑰產生:呼叫
CKM_ML_DSA_KEY_PAIR_GEN,私鑰留在 HSM,回傳CKA_LABEL與公鑰位元組 - 公鑰編碼:以 SPKI 標準格式(AlgorithmIdentifier + BitString)寫入 PEM;AlgorithmIdentifier 的
parametersMUST be absent(RFC 9881 §3) - 簽章:從應用層送入
CKM_ML_DSA的C_SignInit + C_Sign,HSM 內部完成簽章運算,私鑰不離開硬體 - 驗證:軟體側完成(
pqcrypto-mldsa),因驗證只需公鑰,沒有金鑰外洩風險
OID 自動識別:解析 SPKI 時從 AlgorithmIdentifier 對照三組固定 OID 即可決定演算法版本,不需要額外 metadata。
混合憑證 (Hybrid Certificates) — 兩種並存模式
NG-CA 同時實作 RFC 9794 定義的兩種 hybrid 模式。這是相對於早期版本(只實作自訂 [ecc_len][ecc_sig][pqc_sig] 連接格式)的重大標準化升級:

| 模式 | NG-CA enum | RFC 9794 術語 | X.509 表現 | 客戶端要求 |
|---|---|---|---|---|
| non-composite | Algorithm::Hybrid | non-composite | 主簽章 + ITU-T v3 alt-sig extensions(OID 2.5.29.72/73/74) | 舊 client 只驗主簽即可連線 |
| composite | Algorithm::HybridDualSignature | composite | 單一複合 OID(1.3.6.1.5.5.7.6.*)+ 兩段簽章串接於單一 BIT STRING | 必須是 hybrid-aware client |
兩種模式分別解決不同情境:
- non-composite(漸進式遷移友好):適合需要與既有 PKI 共存、不能切斷舊裝置連線的場景。BSI 對德國關鍵基礎設施的 hybrid 強制要求恰好對應這條路。
- composite(draft-ietf-lamps-pq-composite-sigs-19):適合純新建的 PQC 基礎設施。簽章與公鑰各是兩段組件的原始串接,直接放進 X.509 既有的 BIT STRING 欄位;靠固定前綴與 per-algorithm label 防跨組合 spoofing。對協議層透明,不需要新增 alt-sig 處理邏輯 —— 但代價是驗證端必須認得那個複合 OID,沒有向下相容的餘地。
Hybrid 金鑰對產生
兩種模式都需要兩組金鑰 — 但因 RSA / ECC / ML-DSA 全部已 HSM-backed,私鑰側其實是兩個 CKA_LABEL:
pub struct HybridKeyPair {
pub traditional: KeyPair, // RSA / ECC / Ed25519,KeyMaterial::HsmLabel
pub pqc: KeyPair, // ML-DSA-44/65/87,KeyMaterial::HsmLabel
}
產生流程:
- 呼叫
EccProvider::generate()(或 RSA)→ LunaCKM_*_KEY_PAIR_GEN→KeyMaterial::HsmLabel(label_a) - 呼叫
PqcProvider::generate(MlDsa65)→ LunaCKM_ML_DSA_KEY_PAIR_GEN→KeyMaterial::HsmLabel(label_b) - 組成
HybridKeyPair,DB 只儲存兩個 label 與兩份 SPKI 公鑰
資料庫欄位(cas 表):
algorithm='Hybrid'或'HybridDualSignature'(區分兩種模式)public_key_pem= 傳統公鑰 SPKIpqc_public_key_pem= ML-DSA 公鑰 SPKIprivate_key_bytes/pqc_private_key_bytes= HSM 上對應的CKA_LABELUTF-8 bytes(不是真私鑰)
雙重簽章運算
兩種模式的 sign/verify 介面相同(都吃 tbs_data: &[u8] 回傳 Vec<u8>),但內部封裝不同:
non-composite (Algorithm::Hybrid) —— 兩段獨立 BIT STRING,各自寫入 X.509 不同欄位:
(主) signatureValue = Sign_ECDSA(tbsCertificate)
(副) altSignatureValue = Sign_MLDSA65(tbsCertificate)
副簽放入 v3 extension 2.5.29.74;對應的演算法 OID 放入 2.5.29.73;對應的 ML-DSA 公鑰放入 2.5.29.72(subjectAltPublicKeyInfo)。
composite (Algorithm::HybridDualSignature) —— 兩段組件原始串接,不再包 ASN.1 結構:
signatureValue BIT STRING = mldsaSig || tradSig
subjectPublicKey BIT STRING = mldsaPubKey || tradPubKey
draft-19 刻意採用「非 ASN.1」的串接編碼:ML-DSA 的公鑰與簽章長度固定(ML-DSA-65 為 1,952 與 3,309 bytes),解碼時依固定長度切開即可,剩下的就是傳統組件。早期草案曾用 SEQUENCE SIZE (2) OF BIT STRING,已被移除 —— 這是對齊 composite 時最容易踩到的版本差異,若拿舊草案的實作互通會直接解析失敗。
訊息 representative 的構造:composite 要求兩個組件簽同一份 M',而不是原始訊息。以 1.3.6.1.5.5.7.6.45(id-MLDSA65-ECDSA-P256-SHA512)為例:
M' = Prefix || Label || len(ctx) || ctx || SHA-512(tbs_data)
Prefix = "CompositeAlgorithmSignatures2025" -- 固定 32 bytes ASCII,全體 composite 共用
Label = "COMPSIG-MLDSA65-ECDSA-P256-SHA512" -- 每個 composite OID 各一組
ctx = 應用層 context,最長 255 bytes,X.509 簽發時通常為空
mldsaSig = ML-DSA-65.Sign(sk_mldsa, M', ctx = Label)
tradSig = ECDSA-P256-SHA256.Sign(sk_ecdsa, M')
output = mldsaSig || tradSig
關鍵在 Label:它同時綁進 M' 與底層 ML-DSA 的 ctx,讓組件簽章無法被拆出來挪用到別的 OID 或非 composite 情境。注意區分兩者的角色 —— Prefix 是所有 composite 演算法共用的固定字串,真正區分演算法組合的是 Label,不是 per-OID 的隨機 domain separator。
依 draft-19 附錄 A,這組 OID 的公鑰為 2,017 bytes、簽章最大 3,381 bytes(ECDSA 的 DER 編碼會因去除前導零而有數 bytes 浮動)。
驗證:non-composite 由憑證解析器先處理 v3 extension、再分別走兩個 verifier;composite 由 OID 直接派發到 CompositeVerifier,內部解 SEQUENCE 後並行驗證 — 任一失敗整體失敗(沒有「向下相容只驗一邊」的選項)。詳細決策樹見下節。
X.509 延伸欄位(ITU-T X.509 Extensions)
標準 X.509 憑證結構:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
extensions [3] EXPLICIT Extensions OPTIONAL
}
混合憑證延伸欄位(依 ITU-T X.509 (2019) §9.8):
這三個擴充來自 ITU-T X.509 第八版,不是 IETF 規範。曾經對應的 IETF 草案 draft-truskovsky-lamps-pq-hybrid-x509 已經終止,因此引用 alt 擴充時應指向 ITU-T 而非 IETF LAMPS。
extensions [3] EXPLICIT SEQUENCE {
-- 1. Subject Alt Public Key Info (OID 2.5.29.72)
Extension {
extnID = id-ce-subjectAltPublicKeyInfo,
critical = FALSE,
extnValue = OCTET STRING (containing ML-DSA-65 public key)
},
-- 2. Alt Signature Algorithm (OID 2.5.29.73)
Extension {
extnID = id-ce-altSignatureAlgorithm,
critical = FALSE,
extnValue = AlgorithmIdentifier (ML-DSA-65 algorithm)
},
-- 3. Alt Signature Value (OID 2.5.29.74)
Extension {
extnID = id-ce-altSignatureValue,
critical = FALSE,
extnValue = BIT STRING (ML-DSA-65 signature)
}
}
混合憑證建構邏輯(使用 x509-cert crate):
關鍵 OID 定義:
2.5.29.72→ Subject Alt Public Key Info2.5.29.73→ Alt Signature Algorithm2.5.29.74→ Alt Signature Value2.16.840.1.101.3.4.3.17→ ML-DSA-44 演算法2.16.840.1.101.3.4.3.18→ ML-DSA-65 演算法2.16.840.1.101.3.4.3.19→ ML-DSA-87 演算法
ML-DSA OID 來源:RFC 9881(前身為 draft-ietf-lamps-dilithium-certificates,已於 2025 年出 RFC)
建構流程:
- 準備 TBS Certificate:序列化 TbsCertificate 為 DER 格式
- 產生雙重簽章:呼叫 DualSignatureProvider 產生複合簽章
- 解析簽章:從複合簽章中分離 ECC 與 PQC 簽章
- 添加三個 X.509 延伸:
Subject Alt Public Key Info:儲存 ML-DSA-65 公鑰Alt Signature Algorithm:標記 ML-DSA-65 演算法 OIDAlt Signature Value:儲存 PQC 簽章
- 組合憑證:
- 主
signatureAlgorithm使用 ECDSA with SHA-256 - 主
signature使用 ECC 簽章 - Extensions 包含 PQC 公鑰與簽章
- 主
憑證驗證決策樹:
NG-CA 的 verify_certificate() 依 signatureAlgorithm OID 與 v3 extensions 分派至三條路徑 — 涵蓋 single signature、non-composite hybrid、composite hybrid 三種情境:

三條路徑摘要:
- Lane A(Single Signature):傳統 OID 且無 alt-sig extensions,依 OID 派發 RSA / ECDSA / ML-DSA / SLH-DSA 對應 verifier。最常見,路徑最短。
- Lane B(non-composite Hybrid):傳統 OID 但有 v3 alt-sig extensions(
2.5.29.72/73/74)。先驗主簽(向下相容老 client),再從 extensions 取 PQC 公鑰與 alt-sig 補上量子安全層。如果 client 看不懂 alt-sig 它仍能信任主簽,憑證 graceful degradation。 - Lane C(composite Hybrid):
signatureAlgorithm是 composite OID(1.3.6.1.5.5.7.6.*)。先依 OID 查到演算法配對與 label,按固定長度切開串接的兩段簽章,重算M'後兩個 verifier 並行驗證;任一失敗整體失敗。沒有向下相容,這是設計選擇。
完整鏈驗證(/api/v1/certificates/{serial}/verify-chain)會遞迴對 End-Entity → Intermediate → Root 每一層套用上述判斷,pathLenConstraint 也在每層檢查;moka cache(10k entries / 5min TTL)加速重複查詢。
CA 功能實作
Root CA 建立
API 端點: POST /api/v1/ca
請求體:
{
"name": "Example Root CA",
"algorithm": "Hybrid", // Rsa2048/EccP256/MlDsa65/Hybrid
"is_ca": true, // BasicConstraints.cA
"path_len": 2, // 允許 2 層 Intermediate CA
"default_days": 3650, // 預設憑證有效期 10 年
"use_v3_extensions": true,
"default_key_usage": "digitalSignature,keyCertSign,cRLSign",
"default_extended_key_usage": "serverAuth,clientAuth"
}
後端處理流程:
-
金鑰對產生:
- 根據
algorithm參數選擇對應的 Provider(Hybrid/MlDsa65/EccP256/Rsa2048) - 呼叫對應 Provider 的
generate()方法產生金鑰對 - 包裝成
KeyPairEnum統一介面
- 根據
-
自簽憑證建構:
- 產生隨機序號(20 bytes)
- 設定 Subject/Issuer(自簽相同)
- 計算有效期(
default_days天數) - 添加 BasicConstraints 延伸(
ca與path_len) - 添加 KeyUsage 延伸(CA 需
digitalSignature, keyCertSign, cRLSign) - 若為混合憑證,呼叫
build_hybrid_certificate()添加 PQC 延伸
-
資料庫儲存:
- 插入
cas表,儲存金鑰對(傳統與 PQC) - 儲存配置(
is_ca,path_len,default_days, Key Usage) - 取得
ca_id(auto-increment)
- 插入
-
稽核日誌:
- 記錄
create_ca操作 - 包含 CA 名稱、演算法、操作結果
- 記錄
-
回傳結果:
ca_id、CA 名稱、憑證 PEM、演算法類型
自簽憑證建構邏輯:
- 已整合至上述「後端處理流程」的第 2 步驟
Intermediate CA 支援
CA 階層架構(透過 parent_ca_id 與 path_len 控制):
| CA 層級 | parent_ca_id | path_len | 說明 |
|---|---|---|---|
| Root CA | NULL | 2 | 允許建立 2 層子 CA |
| Intermediate CA (L1) | 1 | 1 | 允許建立 1 層子 CA |
| Intermediate CA (L2) | 2 | 0 | 無法再建立子 CA(終端 CA) |
憑證鏈驗證邏輯:
- 從目標憑證開始,讀取對應的 CA 資訊
- 使用 CA 公鑰驗證憑證簽章
- 若
parent_ca_id為 NULL,表示到達 Root CA,驗證完成 - 若有
parent_ca_id,讀取父 CA 憑證,重複步驟 2-3 - 迴圈驗證整條憑證鏈,任一環節失敗則回傳 false
CSR 簽發流程
API 端點: POST /api/v1/ca/{ca_id}/issue
處理流程:
-
解析與驗證 CSR:
- 從 PEM 格式解析 CSR
- 驗證 CSR 內建簽章(確保 CSR 未被竄改)
-
讀取 CA 配置:
- 從資料庫讀取 CA 資訊(包含金鑰、策略設定)
-
應用驗證規則(可選):
- 若 CA 設定了
csr_validation_regex,驗證 Subject DN - 支援台灣身份證號/統一編號驗證(詳見下節)
- 若 CA 設定了
-
產生憑證:
- 產生隨機序號(20 bytes)
- 從 CSR 提取公鑰與 Subject
- 使用 CA 私鑰簽署憑證
- 應用 CA 預設的 Key Usage 與有效期
-
儲存與稽核:
- 插入
certificates表(狀態為 ‘Good’) - 記錄稽核日誌(
issue_cert操作) - 回傳序號與憑證 PEM
- 插入
憑證撤銷與 CRL
撤銷憑證流程:
- 更新
certificates表:設定status = 'Revoked',記錄revoked_at時間戳記 - 檢查影響行數,若為 0 則回傳憑證不存在錯誤
- 記錄稽核日誌(
revoke_cert操作) - 回傳撤銷成功訊息
CRL 產生邏輯:
- 讀取 CA 資訊與私鑰
- 查詢該 CA 所有已撤銷憑證(
status = 'Revoked') - 使用
x509-certcrate 建構 CertificateList:- 設定 CRL 簽發者(CA 名稱)
- 設定 CRL 有效期(預設 7 天)
- 逐一添加 RevokedCert 項目(序號 + 撤銷時間)
- 使用 CA 私鑰簽署 CRL
- 轉換為 PEM 格式回傳
企業級進階功能
NG-CA 在 2026 年初補上五項:Prometheus 監控、名稱約束、VA 功能增強、RSA Hash 演算法擴展,以及測試補強。
Prometheus 監控系統
實現時間:2026 年 2 月 NG-CA 整合完整的 Prometheus 監控指標系統,提供生產環境所需的全面可觀測性:
監控指標類別(25+ 個關鍵指標):
| 類別 | 指標範例 | 用途 |
|---|---|---|
| OCSP 服務 | ocsp_requests_totalocsp_request_duration_seconds | OCSP 請求量、響應延遲 Nonce 使用率追蹤 |
| CRL 管理 | crl_generated_totalcrl_size_bytes | Full/Delta CRL 生成統計 CRL 大小分布 |
| 憑證管理 | certificates_issued_totalcertificates_active | 簽發總數(按演算法分類) 活動憑證數量 |
| 效能指標 | cache_hit_rate_percentdb_query_duration_seconds | 快取命中率 資料庫查詢延遲 |
API 端點:
GET /metrics- Prometheus 格式指標導出
效能影響:
- CPU 開銷:<1%
- 記憶體使用:+10-50MB
- 每請求延遲:~1-5μs
使用場景:
- 生產環境性能監控和 SLA 追蹤
- 容量規劃與異常檢測
- Grafana Dashboard 視覺化
名稱約束 (Name Constraints)
實現時間:2026 年 2 月 實現 RFC 5280 名稱約束功能,允許 CA 限制其簽發的憑證只能用於特定網域、IP 或 Email 範圍:
支援的約束類型:
| 約束類型 | 範例 | 驗證邏輯 |
|---|---|---|
| DNS 網域 | *.example.com | 支援 wildcard 匹配 |
| IP 位址 | 192.168.0.0/162001:db8::/32 | 支援 IPv4/IPv6 CIDR |
*@example.com | 支援 pattern 匹配 |
工作流程:
- CA 建立時設定約束:在
permitted_subtrees/excluded_subtrees欄位指定規則 - CSR 解析:提取 Subject CN 和所有 SAN(DNS、IP、Email)
- 簽發驗證:自動檢查 CSR 內容是否符合名稱約束
- 錯誤回報:明確指出違反哪一條約束規則
安全效益:
- 防止 Intermediate CA 簽發超出授權範圍的憑證
- 支援企業內部 CA 的網域隔離策略
- 符合 RFC 5280 §4.2.1.10 標準
VA 功能增強套件
實現時間:2026 年 2 月
完成憑證驗證功能:
1. OCSP Nonce 擴展(RFC 6960 §4.4.1)
功能:防止 OCSP 重放攻擊
- 在 OCSP 請求中提取 Nonce 擴展
- 在響應中回顯相同的 Nonce
- 確保響應的時效性
安全效益:攻擊者無法重用舊的 OCSP 響應
2. 憑證狀態快取機制
技術實現:
- 使用
moka記憶體快取庫 - TTL:5 分鐘
- 容量:10,000 條目
效能提升:
- OCSP 查詢速度提升 10 倍
- 資料庫負載降低 80-90%
3. 憑證路徑驗證 API(RFC 5280 §6)
功能:完整的憑證鏈驗證
- 遞迴建構信任鏈(End-Entity → Intermediate → Root)
- 路徑長度約束檢查(
pathLenConstraint) - 驗證每一層簽章有效性
API 端點:
GET /api/v1/certificates/{serial}/verify-chain
使用場景:
- 驗證憑證是否由信任的 Root CA 簽發
- 檢查憑證鏈的完整性
- 偵測中間人攻擊
4. Delta CRL 支援(RFC 5280 §5.2.4)
功能:增量撤銷清單生成
- 只包含自上次 Full CRL 後的新撤銷憑證
- 版本追蹤系統(Base CRL Number)
- 支援標準和混合簽章
效能優化:
- CRL 傳輸量減少 95-99%
- 降低頻寬成本和客戶端處理負擔
API 端點:
GET /api/v1/ca/{id}/crl/delta
RSA Hash 演算法擴展
實現時間:2026 年 2 月
擴展 RSA 簽章的 hash 演算法支援,適用於所有 RSA 金鑰大小(2048/3072/4096):
新增支援:
- SHA-384
- SHA-512
- SHA3-384
- SHA3-512
應用場景:
- 高安全需求環境(政府、金融機構)
- 符合特定合規要求(如 CNSA 2.0)
- RSA-4096 與 SHA-512 組合提供 256-bit 安全等級
測試通過率提升
成果:
- 2026 年 1 月:71/71 測試通過(100%)
- 整體測試涵蓋率:99%+
測試類別:
- 密碼學核心測試(11/11)
- OCSP 增強測試(全部通過)
- Delta CRL 測試(全部通過)
- 路徑驗證測試(全部通過)
- 名稱約束測試(全部通過)
ACME v2 協議整合
ACME 協議概述
ACME (Automatic Certificate Management Environment) 是由 Let’s Encrypt 推動的自動化憑證管理協議,已標準化為 RFC 8555。
ACME v2 完整流程(HTTP-01 Challenge):
NG-CA 已實作 RFC 8555 主要端點,目前以 HTTP-01 為主要驗證方式。完整訊息流程如下:

關鍵實作細節:
- JWS 防重放:每個請求帶上一個
nonce(從acme_nonces表取出後立即標記為 used),任何重複使用的 nonce 都會被拒絕。 - JWS 演算法:目前生產支援 ES256(ECDSA P-256)與 RS256(RSA-2048+ PKCS#1 v1.5);EdDSA 規劃中。
- HTTP-01 驗證:NG-CA server 主動向
http://<domain>/.well-known/acme-challenge/{token}發 GET,比對key authorization = token || '.' || base64url(SHA-256(JWK))。 - 狀態機:
acme_orders.status走pending → ready → processing → valid|invalid,每次轉換寫入audit_logs。 - PQC roadmap:composite OID 的 ACME 簽發已可透過 finalize 階段傳入 hybrid CSR;CA/B Forum 對 TLS BR 的 PQC 修訂預計 2026-2027 落地後可開放公開簽發。
- 為什麼非要 ACME 自動化:TLS / Code Signing 整個產業正快速縮短憑證有效期 — Code Signing 從 39 月降到 460 天、TLS 預計 2029 年降至 47 天。NG-CA 把 ACME v2 列為核心模組,正是因為人工續期在 47-day 週期下根本不可行;PQC 演算法輪替(ML-DSA → SLH-DSA / FN-DSA)疊加在這條趨勢上,自動化簽發 + 撤銷管線是 PQC CA 不可妥協的基礎能力。
支援的 ACME 端點
實作狀態(backend/src/acme/routes.rs):
| 端點 | 方法 | 功能 | 狀態 |
|---|---|---|---|
/acme/directory | GET | 取得 ACME 目錄 | 已完成 |
/acme/new-nonce | HEAD/GET | 取得 nonce | 已完成 |
/acme/new-account | POST | 註冊帳號 | 已完成 |
/acme/acct/{id} | POST-as-GET | 查詢帳號資訊 | 已完成 |
/acme/new-order | POST | 建立訂單 | 已完成 |
/acme/authz/{id} | POST-as-GET | 查詢授權狀態 | 已完成 |
/acme/chall/{id} | POST | 回應 Challenge | 部分完成 |
/acme/order/{id}/finalize | POST | 完成訂單(提交 CSR) | 規劃中 |
/acme/cert/{serial} | POST-as-GET | 下載憑證 | 已完成 |
JWS 簽名驗證
ACME 要求所有請求都使用 JWS (JSON Web Signature) 簽名(RFC 7515):
支援的演算法:
| 演算法 | 類型 | 金鑰類型 | 狀態 |
|---|---|---|---|
| ES256 | ECDSA with SHA-256 | P-256 | 已完成 |
| RS256 | RSA PKCS#1 v1.5 with SHA-256 | RSA-2048+ | 已完成 |
| EdDSA | Ed25519 | Ed25519 | 規劃中 |
JWS 驗證邏輯:
資料結構:
JwsHeader:包含alg(演算法)、nonce(防重放)、url(請求 URL)、kid(帳號 ID)、jwk(公鑰)Jwk(JSON Web Key):包含kty(金鑰類型)、EC 參數(crv,x,y)或 RSA 參數(n,e)
驗證流程:
-
解析 JWS:
- 格式為
header.payload.signature(三段 Base64 URL編碼) - 檢查格式正確性
- 格式為
-
驗證安全參數:
- 解碼 header 並檢查
nonce(防重放攻擊) - 檢查
url是否匹配請求路徑
- 解碼 header 並檢查
-
簽章驗證(依演算法):
- ES256(ECDSA P-256):
- 從 JWK 解析 P-256 公鑰(
x,y座標) - 使用
p256crate 驗證 ECDSA 簽章
- 從 JWK 解析 P-256 公鑰(
- RS256(RSA PKCS#1 v1.5):
- 從 JWK 解析 RSA 公鑰(
nmodulus,eexponent) - 使用
rsacrate 驗證 PKCS#1 v1.5 簽章
- 從 JWK 解析 RSA 公鑰(
- ES256(ECDSA P-256):
-
解碼 Payload:
- Base64 URL 解碼 payload
- 反序列化為 JSON 結構
ACME 資料庫架構
| 資料表 | 關鍵欄位 | 說明 |
|---|---|---|
| acme_nonces | nonce, created_at, used | 防重放攻擊(nonce 一次性使用) |
| acme_accounts | id, account_key_jwk, contact, status | ACME 帳號(儲存 JWK 公鑰與聯絡方式) |
| acme_orders | id, account_id, status, identifiers | 憑證訂單(狀態:pending/ready/processing/valid/invalid) |
| certificate_serial, expires | 簽發後的憑證序號與訂單過期時間 | |
| acme_authorizations | id, order_id, identifier, status | 域名授權(每個 identifier 一筆記錄) |
| challenge_type, challenge_token | Challenge 類型(http-01/dns-01)與驗證 token | |
| challenge_status, validated_at | Challenge 狀態與驗證時間 |
台灣特色驗證
身份證字號驗證
格式:1 個英文字母 + 9 個數字(範例:A123456789)
驗證邏輯(依內政部規定):
- 格式檢查:長度 10 碼,首碼英文,後 9 碼數字
- 英文字母轉數字:A=10, B=11, …, Z=35(I=34, O=35 為特殊對應)
- 拆分英文數值:轉換後的數字拆成十位數(d1)與個位數(d2)
- 權重計算:
- 權重序列:
[1, 9, 8, 7, 6, 5, 4, 3, 2, 1] - 加權總和:
d1×1 + d2×9 + digit[0]×8 + ... + digit[8]×1
- 權重序列:
- 檢查碼驗證:加權總和
% 10 == 0
測試案例:A123456789 與 F131104093 通過,A123456788 因檢查碼錯誤被拒。
統一編號驗證
格式:8 個數字(範例:12345678)
驗證邏輯(依財政部規定):
- 格式檢查:長度 8 碼,全數字
- 權重計算:
- 權重序列:
[1, 2, 1, 2, 1, 2, 4, 1] - 每個
digit[i] × weight[i]的乘積拆分為十位數與個位數後加總
- 權重序列:
- 特殊規則:
- 若第 7 碼為 7,則
sum % 10 == 0或(sum + 1) % 10 == 0 - 其他情況:
sum % 10 == 0
- 若第 7 碼為 7,則
CSR Subject 驗證整合
驗證流程:
- 從 CSR 的 Subject DN 中解析 CN (Common Name)
- 根據 CA 的
csr_validation_regex設定選擇驗證方法:"TW_ID"→ 台灣身份證字號驗證"TW_TAX_ID"→ 統一編號驗證"TW_ORG_ID"→ 組織機構代碼驗證- 自定義 regex → 使用正則表達式比對 CN
使用情境:
- CA 管理員在建立 CA 時設定
csr_validation_regex - 簽發憑證時自動檢查 CSR 的 CN 是否符合規則
- 驗證失敗則拒絕簽發憑證
前端管理介面
Vue 3 技術棧
核心依賴:
- Vue 3.5 + TypeScript:組件化架構
- Vue Router 4:頁面路由管理
- Pinia 3:狀態管理
- Axios:HTTP 客戶端(含自動重試)
- Vite 6:開發伺服器與建置工具
- Tailwind CSS 4:樣式框架
- Lucide Icons:圖示庫
儀表板組件
主要功能 (Dashboard.vue):
資料展示:
- 統計卡片:顯示 CA 總數、已簽發憑證數、已撤銷憑證數(使用
StatCard可復用組件) - CA 列表表格:展示所有 CA,包含名稱、演算法類型(顏色徽章)、CA 類型、建立時間
互動功能:
onMounted:載入統計資料與 CA 列表(呼叫/api/v1/stats與/api/v1/ca)algorithmBadgeClass():根據演算法類型回傳對應的 Tailwind CSS class(Hybrid=紫色、ML-DSA=藍色、ECC=綠色、RSA=灰色)downloadCRL():下載指定 CA 的 CRL(呼叫/api/v1/ca/{id}/crl,透過 Blob 觸發瀏覽器下載)
實際介面截圖
以下是 NG-CA 管理介面的實際畫面:
Dashboard 儀表板

主要功能:
- 統計卡片:顯示 CA 總數、已簽發憑證數、已撤銷憑證數
- CA 列表:展示所有 CA,標示演算法類型(Hybrid/ML-DSA/ECC)
- 快速操作:下載 CRL、查看 CA 詳情
CA 列表視圖

顯示資訊:
- CA 名稱與 ID
- 演算法類型(使用顏色徽章區分)
- 紫色:Hybrid (ECC + ML-DSA)
- 藍色:ML-DSA-65
- 綠色:ECC P-256
- CA 類型(Root CA / Intermediate CA)
- 建立時間
- 操作按鈕(下載憑證、下載 CRL、撤銷)
憑證列表視圖

顯示資訊:
- 憑證 Subject(Common Name)
- 簽發 CA
- 有效期限(起始/結束日期)
- 憑證狀態(有效 / 已撤銷 / 即將過期)
- 操作(下載憑證、撤銷憑證、查看詳情)
CA 建立對話框
表單欄位 (CreateCaDialog.vue):
- CA 名稱:文字輸入(必填)
- 演算法選擇:下拉選單(Hybrid/ML-DSA-65/ECC P-256/RSA-2048)
- CA 類型:核取方塊(是否為 CA 憑證)
- 路徑長度限制:數字輸入(僅 CA 憑證,0-5 層)
- 預設有效期:數字輸入(1-3650 天)
互動邏輯:
handleSubmit():呼叫POST /api/v1/ca建立 CA- 成功後顯示 Toast 通知,關閉對話框,重新載入頁面
- 錯誤處理:顯示 API 回傳的錯誤訊息或預設「建立失敗」
- 提交期間禁用按鈕,顯示「建立中。..」載入狀態
安全性與合規
FIPS 140-3 對齊(HSM 整合後現況)
2026 年初的 Phase A-C HSM 整合完成後,FIPS 140-3 遵循度從原型期的 2/10 大幅提升 — 因為所有受 FIPS 規範的密碼學運算都委派給 Thales Luna HSM(FIPS 140-3 Level 3 認證模組),NG-CA 應用層只剩編排與資料整合:
| 項目 | 現況 | 認證來源 |
|---|---|---|
| 密碼學模組 | RSA / ECC / ML-DSA / ML-KEM 在 Luna 內運算 | Luna HSM FIPS 140-3 Level 3 |
| 金鑰儲存 | CKA_LABEL only;私鑰永不離開 HSM | HSM 物理保護 |
| 隨機數產生 | Luna HSM 內部的 hardware RNG(FIPS 通過) | Luna 認證 |
| 軟體 PQC | SLH-DSA / FN-DSA 走 RustCrypto/pqcrypto | NIST 標準參考實作(待 CMVP 認證) |
| 自我測試 | Luna 開機自檢;NG-CA 啟動時 connection probe | HSM POST + 應用層健檢 |
| 稽核 | SOC2-ready audit.db,每次 sign / issue / revoke 寫入 | RFC 5424 等級結構化日誌 |
| 演算法支援 | 全 FIPS 186-5 + FIPS 203/204;SLH-DSA 走軟體實作,FN-DSA 待 FIPS 206 定案 | NIST 最新標準 |
剩餘缺口(路線圖):
- SLH-DSA / FN-DSA HSM 化:等待 Luna 後續韌體開放(一旦支援,現有
KeyMaterialenum 切換即可,API 不變) - CMVP 系統認證:Luna 已認證,但「NG-CA + Luna 整合」整體仍需走 ISO/IEC 17025 流程
- FIPS 140-3 Level 4:物理破壞偵測,預計搭配 Luna 高階機型
私鑰生命週期:永不離開 HSM
舊版 NG-CA 計畫用 AES-256-GCM 加密私鑰後存進 SQLite,這個方案在 Phase B 完成後已完全廢棄。所有受 FIPS 規範的金鑰(RSA / ECC / ML-DSA / ML-KEM)走以下生命週期:
1. keygen → Luna PKCS#11 C_GenerateKeyPair
├─ 私鑰留在 HSM partition,標記為 CKA_PRIVATE = true
└─ 應用層只取得 CKA_LABEL(UTF-8 string)與公鑰位元組
2. 儲存 → ng-ca.db.cas.private_key_bytes = CKA_LABEL(不是私鑰!)
3. sign → C_SignInit(CKA_LABEL) + C_Sign(message)
簽章運算在 HSM 內完成;應用層只看到輸出 bytes
4. 銷毀 → C_DestroyObject(CKA_LABEL);DB 行同步刪除
KeyMaterial enum 把這層語意搬到型別系統 — 若呼叫端嘗試把 HsmLabel(...) 餵進 PKCS#8 解析器,會在編譯期被擋下。對 SLH-DSA / FN-DSA 等仍走軟體實作的演算法,私鑰才以 KeyMaterial::Software(...) 形式儲存(短期內仍考慮上層加密儲存)。
生產環境的 HSM 配置:
HSM_MODULE_PATH=/usr/safenet/lunaclient/lib/libCryptoki2_64.so
HSM_SLOT_ID=0
HSM_PIN=<透過 systemd LoadCredential 或 secret manager 注入>
HSM_POOL_SIZE=16
HSM_POOL_SIZE 控制 connection pool 上限(預設 16),實測在 6 vCPU 機器上能達到 ~4000 sig/s(ECDSA P-256)。
測試與驗證
測試涵蓋率
整體統計(2026 年 5 月):71/71 測試通過(100%) — 從 v0.2 的 53/54 升級到 v1.0 後保持滿分
| 測試套件 | 重點驗證 | 位置 |
|---|---|---|
| 密碼學核心 | RSA / ECC / ML-DSA / SLH-DSA / FN-DSA / ML-KEM 完整 keygen-sign-verify | backend/tests/coverage_tests.rs |
| HSM 整合測試 | luna-cryptoki keygen、CKM_* mechanism、connection pool | backend/tests/hsm_*.rs |
| Hybrid 雙模式 | non-composite + composite 兩條路徑各自簽發/驗證 | backend/tests/hybrid_*.rs |
| API 端點 | CA CRUD、issue、revoke、verify-chain | backend/tests/api_tests.rs |
| ACME 協議 | 完整 RFC 8555 流程 + JWS ES256 / RS256 | backend/tests/acme_*.rs |
| OCSP 增強 | Nonce 擴展、moka cache、next_update | backend/tests/ocsp_enhanced_tests.rs |
| Delta CRL | base CRL number、增量產出 | backend/tests/delta_crl_tests.rs |
| 路徑驗證 | 多層 chain、pathLenConstraint | backend/tests/path_validation_tests.rs |
| 名稱約束 | DNS / IP / Email permitted/excluded | backend/tests/name_constraints_tests.rs |
| 負面測試 | 偽造簽章、過期憑證、撤銷後存取 | backend/tests/negative_tests.rs |
密碼學核心測試
測試案例涵蓋:
ML-DSA-65 完整工作流程測試 (test_ml_dsa_65_full_workflow):
- 金鑰對產生:驗證公鑰 PEM 與私鑰 bytes 非空,私鑰大小為 4000 bytes
- 簽章產生:驗證簽章大小為 3293 bytes(ML-DSA-65 規格)
- 簽章驗證:驗證合法簽章通過
- 負面測試:驗證偽造訊息的簽章驗證失敗
混合雙重簽章測試 (test_hybrid_dual_signature):
- 混合金鑰對產生:包含 ECC 與 ML-DSA 兩組金鑰
- 雙重簽章:產生複合簽章(composite 模式為
mldsaSig || tradSig串接) - 驗證複合簽章格式:ML-DSA 段長度固定,切開後檢查 ECDSA 段長度是否落在合理範圍(~70-72 bytes DER)
- 雙重驗證:驗證兩個簽章都有效
- 負面測試:篡改任一簽章後驗證應失敗
RSA 與 ECC 工作流程測試:
- RSA-2048/3072/4096:驗證金鑰產生、簽章大小(256/384/512 bytes)、簽章驗證
- ECC P-256/384/521:驗證金鑰產生、簽章大小(~64/96/132 bytes)、簽章驗證
X.509 憑證建構測試:
- 基本 X.509 欄位:序號、Subject、Issuer、有效期
- BasicConstraints 延伸:CA 標記、路徑長度限制
- KeyUsage 延伸:digitalSignature、keyCertSign、cRLSign
- 混合憑證延伸:Subject Alt Public Key Info、Alt Signature Algorithm、Alt Signature Value
OpenSSL 互通性驗證(3.5 / 4.0)
OpenSSL 是 PQC 憑證能否被真實世界接受的第一道關卡。它的 PQC 支援時間線:
| 版本 | 發布 | 與 PQC CA 相關的變化 |
|---|---|---|
| 3.5(LTS,支援至 2030-04) | 2025-04-08 | 原生 ML-KEM / ML-DSA / SLH-DSA,不需 provider;TLS 預設群組改以 hybrid KEM 為先 |
| 3.6 | 2025-10 | CMS 支援 KEMRecipientInfo(RFC 9629)與 ML-KEM;FIPS provider 對 SLH-DSA 金鑰匯入做 PCT |
| 4.0 | 2026-04-14 | 新增 ML-DSA-MU(external-mu)摘要、LMS 簽章驗證與 SPKI 編解碼;CMS 支援無外部摘要的簽章方案(ML-DSA / Ed25519);TLS 預設 sigalg 把三個 ML-DSA 排在最前,並改用 IANA 正式名稱 |
撰稿時最新修補版為 4.0.2 與 3.6.4(皆 2026-08-25)。以下三組實測在 OpenSSL 3.5.5 上執行。
實測一:pure ML-DSA 憑證完全互通
openssl genpkey -algorithm ML-DSA-65 -out mldsa65-ca.key
openssl req -new -x509 -key mldsa65-ca.key -days 365 \
-subj "/CN=NG-CA ML-DSA-65 Root" -out mldsa65-ca.pem
openssl verify -CAfile mldsa65-ca.pem mldsa65-ca.pem
# mldsa65-ca.pem: OK
ML-DSA 是 one-shot 簽章、不吃外部摘要,所以不能加 -sha256;OpenSSL 會直接把 signatureAlgorithm 寫成 ML-DSA-65(OID 2.16.840.1.101.3.4.3.18,依 RFC 9881)。
這張根憑證的 DER 是 5,532 bytes。同樣欄位的 ECDSA P-256 根憑證只要數百 bytes —— 憑證鏈膨脹不是理論問題,是量出來的。
實測二:non-composite hybrid 憑證驗得過,但 alt 簽章被忽略
把 ML-DSA-65 的 SPKI、演算法 OID 與 alt 簽章分別放進 2.5.29.72/73/74,主簽仍是 ECDSA P-256:
openssl verify -CAfile hybrid-ca.pem hybrid-ca.pem
# hybrid-ca.pem: OK
openssl x509 -in hybrid-ca.pem -noout -text
# X509v3 Subject Alternative Public Key Info:
# 0...0...`.H.e...........
# X509v3 Alternative Signature Algorithm:
# 0...`.H.e....
# X509v3 Alternative Signature Value:
# .......)....9..T.P.Tk...b6...
兩個結論:
- OpenSSL 認得這三個擴充的名稱(OID 表內已收錄),但只把內容當不透明位元組印出來 —— 它不解碼、不驗證 alt 簽章。
openssl verify會通過,是因為三個擴充標記為 non-critical,OpenSSL 依 RFC 5280 忽略未處理的非關鍵擴充。這正是 non-composite 模式「向下相容」的機制本體:舊驗證器不是懂得略過,而是根本沒看見。
代價要算清楚。那張憑證從 ECDSA 的數百 bytes 漲到 5,758 bytes,卻沒有為 OpenSSL 端帶來任何量子安全性 —— 多出來的 5kB 只是為了讓未來的驗證器有東西可驗。要真正拿到量子安全,驗證方必須自行實作 ITU-T X.509 (2019) §9.8 的流程:重組 TBS、剔除 2.5.29.74 後以 alt 公鑰驗章。NG-CA 的 Lane B 做的就是這件事。
實測三:composite 目前沒有 OpenSSL 生態可互通
composite 的處境正好相反 —— 格式乾淨,但沒人驗得動:
- OpenSSL 沒有原生 composite ML-DSA:3.5.5 上
openssl list -signature-algorithms沒有任何1.3.6.1.5.5.7.6.*,4.0 的 CHANGES 也未新增(4.0 提到的 composite 指的是RSA-SHA2-256這類「演算法+摘要」組合,與 PQ composite 無關)。 - oqs-provider 自 0.10.0 起移除了 composite 簽章支援(並在 OpenSSL ≥ 3.5 環境下停用自身的 ML-KEM / ML-DSA,以避免與原生實作衝突)。曾經能用 oqs-provider 驗 composite 憑證的做法,現在已經行不通。
- 目前 composite 的主要互通對象是 Bouncy Castle(Java 與 C# 均已支援 IANA 正式 OID)以及建構於其上的 EJBCA。
把三組實測攤成一張矩陣,落差的位置就很清楚:

最右欄與最左欄的對比是整篇文章的重點。務實的結論是:今天要跟 OpenSSL 世界互通,pure ML-DSA 比任何 hybrid 模式都順。hybrid 的價值在遷移期的風險管理,不在互通性。
composite 對 HSM 的隱性約束
draft-19 的序列化規定會直接反噬 HSM 設計。composite 私鑰定義為 ML-DSA 的 32-byte seed 與傳統私鑰的串接,而且規範明文禁止在 composite 與 non-composite 之間、或兩個 composite 之間重用任何組件金鑰材料。兩個後果:
- 若 HSM 只保存展開後的 ML-DSA 私鑰、不支援 seed 形式的產生與匯出,composite 金鑰就無法用標準格式備份或遷移到別的實作。
- 「同一把 ML-DSA 金鑰同時服務兩種 hybrid 模式」這種省事做法不合規,兩種模式必須各自 keygen、各自持有獨立的 HSM label。
這類約束在草案文字裡只佔幾行,但決定了 HSM 整合能不能做。導入 composite 前,先確認韌體的 CKM_ML_DSA_KEY_PAIR_GEN 是否支援 seed 語意。
CSR 與憑證簽發互通
一般的 ACME / CSR 流程不需要任何特殊處理:
# 用 OpenSSL 產生 ML-DSA-65 的 CSR
openssl genpkey -algorithm ML-DSA-65 -out ee.key
openssl req -new -key ee.key -subj "/CN=pqc.example.tw" -out ee.csr
# 送進 NG-CA 的 POST /api/v1/ca/{ca_id}/issue,取回 certificate_pem 後驗鏈
openssl verify -CAfile ca.pem ee.pem
回歸測試腳本(scripts/verify_openssl.sh)把上述三組實測固定下來:取得 Root CA 憑證、以 openssl x509 檢查結構、以 openssl verify 驗自簽與憑證鏈、產生 CSR 並簽發、確認 alt 擴充存在且不影響驗證結果。
未來發展
SLH-DSA / FN-DSA HSM 化
目標時程:跟隨 Luna 韌體 roadmap(Thales 預計 2026-2027 上線)
主要工作項目:
- 等待 Luna 釋出
CKM_SLH_DSA_*與CKM_FN_DSA_*mechanism - 在
luna-cryptokicrate 補上對應 wrapper - 把
SlhDsaProvider/FnDsaProvider改為 dispatch(軟體 fallback + HSM 優先) - 應用層完全無感(
KeyMaterial::Software→HsmLabel自動切換)
CA/B Forum TLS BR PQC 對齊
現況(2026-09):CA/B Forum 尚無通過的 Baseline Requirements ballot 允許 ML-DSA 用於公開信任的 TLS 憑證。TLS 層的簽章協商規範(draft-ietf-tls-mldsa)已獲 IESG 核准,但憑證政策這一端還沒動。
關鍵未定點:
- 公開 TLS 簽發是否允許 hybrid(vs. pure PQC)
- ML-DSA-44 vs ML-DSA-65 vs ML-DSA-87 的選用基準
- 憑證鏈總大小上限(影響 OCSP stapling 必要性)
- Merkle Tree Certificates 會不會繞過 X.509 路線,讓 composite 在公開 PKI 失去舞台
NG-CA 準備:composite OID 已實作完成,但暫不開放公開 TLS 簽發;私有 CA、企業 mTLS 與 S/MIME 場景已可用。
gRPC API
動機:支援微服務架構與高效能 RPC
API 設計(proto/ca.proto):
syntax = "proto3";
package ng_ca.v1;
service CaService {
rpc CreateCa(CreateCaRequest) returns (CreateCaResponse);
rpc IssueCertificate(IssueCertificateRequest) returns (IssueCertificateResponse);
rpc RevokeCertificate(RevokeCertificateRequest) returns (RevokeCertificateResponse);
rpc GetCRL(GetCRLRequest) returns (GetCRLResponse);
}
message CreateCaRequest {
string name = 1;
Algorithm algorithm = 2;
bool is_ca = 3;
optional uint32 path_len = 4;
uint32 default_days = 5;
}
enum Algorithm {
ALGORITHM_UNSPECIFIED = 0;
RSA_2048 = 1;
RSA_3072 = 2;
RSA_4096 = 3;
ECC_P256 = 4;
ECC_P384 = 5;
ECC_P521 = 6;
ML_DSA_44 = 7;
ML_DSA_65 = 8;
ML_DSA_87 = 9;
HYBRID = 10;
}
message CreateCaResponse {
int64 ca_id = 1;
string certificate_pem = 2;
}
message IssueCertificateRequest {
int64 ca_id = 1;
string csr_pem = 2;
uint32 days = 3;
bool is_ca = 4;
optional uint32 path_len = 5;
}
message IssueCertificateResponse {
string serial_number = 1;
string certificate_pem = 2;
}
多租戶支援
資料隔離設計:
-- 新增租戶表
CREATE TABLE tenants (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
api_key TEXT NOT NULL UNIQUE,
created_at TEXT DEFAULT (datetime('now'))
);
-- CA 表添加租戶關聯
ALTER TABLE cas ADD COLUMN tenant_id INTEGER REFERENCES tenants(id);
-- 憑證表添加租戶關聯
ALTER TABLE certificates ADD COLUMN tenant_id INTEGER REFERENCES tenants(id);
-- Row-Level Security
CREATE INDEX idx_ca_tenant ON cas(tenant_id);
CREATE INDEX idx_cert_tenant ON certificates(tenant_id);
API 認證邏輯(tenant_auth_middleware):
驗證流程:
- 提取 API Key:從請求標頭
X-API-Key取得金鑰 - 查詢租戶:使用 API Key 從
tenants表查詢對應的租戶 - 權限驗證:若 API Key 不存在或無效,回傳
401 Unauthorized - 注入租戶 ID:將租戶 ID 注入請求延伸(
req.extensions_mut()) - 繼續處理:將請求傳遞給下一個 middleware 或 handler
資料隔離:所有 CA 與憑證查詢自動過濾 tenant_id,確保租戶間資料完全隔離
結語與資源
關鍵要點
- 兩種 hybrid 模式並存:對齊 RFC 9794 —
Algorithm::Hybrid(non-composite,向下相容)+Algorithm::HybridDualSignature(composite,draft-ietf-lamps-pq-composite-sigs-19,已進 RFC Editor queue) - 17 個演算法、5 大類別:RSA / ECC / ML-DSA / SLH-DSA / FN-DSA / ML-KEM 全套,從 FIPS 186-5 到 FIPS 203/204/205(FN-DSA 所依據的 FIPS 206 仍未定案);Hybrid 兩種模式是組合方式,不另計為演算法
- Luna HSM 託管金鑰:應用層只做編排與 ASN.1 組裝,密碼學運算交給 Luna FIPS 140-3 Level 3 硬體,私鑰永不離開 HSM
- 企業級功能完備:Prometheus 監控(25+ 指標)、Name Constraints、Delta CRL、verify-chain API、moka cache
- ACME v2 + JWS:RFC 8555 完整流程,ES256 / RS256 簽章驗證
- 台灣在地化:身份證 / 統編 / 組織機構代碼驗證
- 互通性分場景:pure ML-DSA 與 OpenSSL 3.5+ 原生互通最順;non-composite 能被 OpenSSL 驗過但 alt 簽章被忽略;composite 目前只能與 Bouncy Castle 生態互通
- 生產就緒:71/71 測試通過(100%)、v1.0 Released
專案資源
核心標準:
- FIPS 203 (ML-KEM) · FIPS 204 (ML-DSA) · FIPS 205 (SLH-DSA);FIPS 206 (FN-DSA) 仍在制定
- RFC 9794 — PQ/Traditional Hybrid Terminology(2025-06)
- RFC 9881 — X.509 ML-DSA · RFC 9909 — X.509 SLH-DSA · RFC 9935 — X.509 ML-KEM(2026-03)
- RFC 9882 — ML-DSA in CMS(程式碼簽章 / S/MIME 路線)
- draft-ietf-lamps-pq-composite-sigs-19(已在 RFC Editor queue;NG-CA
HybridDualSignature對齊版本) - ITU-T X.509 (2019) §9.8(alt 擴充
2.5.29.72/73/74的規範來源) - RFC 8555 (ACME v2) · RFC 6960 (OCSP) · RFC 5280 (X.509 / CRL / 路徑驗證)
實作與互通參考:
- OpenSSL 4.0 CHANGES(PQC 支援時間線;3.5 起原生 ML-KEM / ML-DSA / SLH-DSA)
- oqs-provider release notes(0.10.0 移除 composite 簽章支援)
- Bouncy Castle — Composite Signatures(目前 composite 的主要互通對象)
- PKCS#11 v3.0(Luna HSM mechanism 參考)
部署參考:
- Cloudflare 後量子網路現況報告 · Cloudflare Radar — Post-Quantum
- Chromium Post-Quantum PKI 設計文件
- BSI Migration to PQC
雲杉相關文章:
- Luna HSM × Prometheus 監控(這篇講金鑰運維面)
- PQC × Luna HSM 企業級方案
- cbom_scanner — 用 CycloneDX 1.6 盤點密碼學資產(PQC 遷移前的盤點工具)
- Code Signing 憑證縮短至 460 天 — PKI 產業趨勢(TLS 也走向 398→47 天;NG-CA 的 ACME v2 自動化是同一趨勢的解法)
本文最初發表於 2026-02-04,2026-09-11 再次更新
- 新增 OpenSSL 3.5 / 4.0 互通實測(pure ML-DSA、non-composite hybrid、composite 三種情境的實際結果與憑證尺寸),並以互通矩陣圖取代原本的場景表格與首圖
- 新增「標準狀態一覽」:RFC 9881 / 9882 / 9909 / 9935 已發布,composite-sigs draft-19 進入 RFC Editor queue,FIPS 206 仍未出公開草案
- 修正 composite 編碼描述:draft-19 改為兩段簽章原始串接,早期草案的
SEQUENCE OF BIT STRING已移除;補上Prefix/Label的正確角色 - 修正 alt 擴充的規範來源為 ITU-T X.509 (2019) §9.8(對應 IETF 草案已終止)
- 更新瀏覽器與公開 PKI 現況;淡化實作語言的篇幅,改以信任邊界說明架構
- 重畫架構、hybrid 模式、驗證路徑與 ACME 四張圖,統一為單色 teal 色系;原圖的多色分類已與內容脫節
- 演算法矩陣由圖片改為表格,並修正 SLH-DSA-SHA2-192s / 256s 的 OID(原標
.26/.32,應為.22/.24)
如有任何問題或建議,歡迎透過聯絡我們 頁面與我們聯繫。