💡 完整的工作範例可在 GitHub 上取得:
sign-word-with-ml-dsa-certificates-dotnet

舊方式是一個專案計畫

詢問使文件簽署具備後量子安全需要什麼,會得到一條路線圖:評估演算法、選擇函式庫、為簽署程式碼撰寫抽象層、規劃雙簽署期間、預算一季的時間。

對於組織層面——憑證採購、政策、驗證器支援——大部分仍然適用。程式碼層面卻比路線圖所示的要小,這點在任何人為此預算一季之前都值得了解。

ML-DSA 簽署是 GroupDocs.Signature 在 .NET 上的功能,可使用基於 FIPS 204(NIST 後量子簽章標準)的憑證簽署 Word 文件。它在 26.9 版中推出,從呼叫程式碼的角度看,它只是一個不同的 PFX 檔案。

有更好的方法

以下是完整的程式碼變更:

using var signature = new Signature(sourcePath);

var options = new DigitalSignOptions(pfxPath)
{
    Password = certificatePassword
};

SignResult result = signature.Sign(outputPath, options);

這與使用 RSA 憑證時的呼叫相同。演算法是憑證的屬性,因此不需要任何選項來選擇它,也不需要抽象層,過渡期間也不會出現第二條程式碼路徑。將 DigitalSignOptions 指向 ML-DSA PFX,即可產生 ML-DSA 簽章。

在同時使用多個憑證時,從結果中讀回憑證是值得的做法:

var created = result.Succeeded.OfType<DigitalSignature>().FirstOrDefault();
return created?.Certificate?.Subject ?? "(no certificate returned)";

選擇層級,使用數字而非意見

ML-DSA 有三種參數組合,對應 NIST 安全等級 2、3 與 5。更強的等級意味著更大的金鑰與簽章,最實際的決策方式是將自己的文件簽署三次並觀察結果:

var levels = new Dictionary<string, string>
{
    ["ML-DSA-44"] = MlDsa44Pfx,
    ["ML-DSA-65"] = MlDsa65Pfx,
    ["ML-DSA-87"] = MlDsa87Pfx
};

此範例會為每個等級寫入一個簽署副本並記錄其大小,因此權衡是透過測量得出,而非來自規格的表格。對單一合約而言差異不大;但對於數百萬份簽署文件的存檔而言,這是一個容量問題,值得在決定採用最高等級前先詢問。

當沒有政策規定時,ML-DSA-65 是合理的預設。CNSA 2.0 等檔案明確指定 ML-DSA-87,而 ML-DSA-44 只在尺寸比安全邊際更重要時才有意義。

驗證僅需公用憑證

分發方式與 RSA 相同,這是第二個好消息:

var options = new DigitalVerifyOptions(certificatePath);
if (password != null)
{
    options.Password = password;
}

VerificationResult result = signature.Verify(options);

接收者只需要簽署者的 .cer,不需要其他任何東西。只有當簽章與內容匹配且憑證的序號與指紋相符時結果才算有效,因此由不同方簽署的文件會驗證失敗——範例透過兩次驗證來證明:一次使用正確的憑證,另一次使用他人的憑證。

並排比較:預期與實際

遷移計畫假設的內容 26.9 實際需要的內容
程式碼變更 簽署的抽象層 不同的 PFX 路徑
API 介面 新的後量子方法 DigitalSignOptions,未變更
層級選擇 函式庫設定 載入的憑證
驗證 接收者的新工具 簽署者的公用 .cer
平台工作 各作業系統的金鑰處理 無 - 函式庫在內部自行回退
格式支援 所有格式 目前僅支援 Word 格式

最後一列是限制規劃的關鍵,亦導向本文的誠實部分。

尚未支援的項目

兩項限制,皆在承諾之前值得了解。

在 26.9 中,格式支援僅限於 Word——DOCX、DOC、ODT 以及其他 Word 系列。PDF、試算表與簡報無法使用 ML-DSA 簽署。對於以 PDF 為主的流程,此版本僅適合原型設計與測量,而非遷移。

另一項是驗證器支援。目前尚無 ML-DSA 的標準 XML-DSig 識別碼,因此即使簽章在密碼學上是正確且透過 API 能正確驗證,Microsoft Word 仍可能不將其標示為有效。這是標準的缺口而非缺陷,意味著驗證必須在程式碼中完成,而非由審閱者開啟檔案檢視標示。

還有一項平台細節不需任何操作:.NET 在所有環境(包括 .NET 8 的 Linux)皆無法讀取 ML-DSA 金鑰。若無法讀取時,GroupDocs.Signature 會改由 Word 引擎讀取憑證,因此相同的建置可在開發者筆記型電腦與 Linux 容器上執行,無需條件編譯。

鑑於這些限制,現在值得執行嗎?

是的,原因有兩點且與程式碼無關。憑證採購速度緩慢——公共 CA 仍在推行 ML-DSA 發行——因此組織層面的工作越早開始越有利。而「我們今天能產生後量子簽章嗎」是合規團隊開始提出的問題;能以已簽署的文件而非計畫來回應,值得花掉一個下午的時間。

範例實際證明的內容

四個方法依序執行,退出代碼與結果相連。它使用 ML-DSA-65 簽署合約並印出所使用憑證的主旨。它以三個等級簽署相同合約並印出產生的大小。它對簽署檔案進行兩次驗證——一次使用簽署者的公用憑證,預期成功;一次使用其他簽署者的憑證,預期失敗。最後列出輸出檔案中找到的數位簽章。

第二次驗證才是值得複製的部分。只展示過有效輸入的例行程式無法告訴你它是否會拒絕無效輸入,而對於簽章而言,這就是全部問題所在。

真實案例:三十年合約

長期保存的檔案庫使此議題不再是理論。今天簽署且保存三十年的合約必須在此期間的任何密碼學變化下仍能驗證,而「先收集、後解密」正是針對此類資料的文件化威脅模型。

對於此類檔案庫,實務上今天的做法是雙軌並行:對於 ML-DSA 尚未支援的格式仍使用 RSA,開始以 ML-DSA-65 或 87 簽署 Word 輸出,並記錄每份文件使用的演算法,以便未來稽核時能在不開啟檔案的情況下辨識。

複製範例前需修正的一件事

此儲存庫隨附自行簽署的 ML-DSA 憑證,使示範可直接執行,亦即在 documents/ 中放置了四個 PFX 檔案與硬編碼密碼。對於僅在此範例內有效的臨時測試憑證而言,這是可以接受的。

這不應成為你自己的儲存庫的做法。應改為在執行時產生測試憑證,像 GroupDocs 的 certificate‑validity 範例那樣,或完全將其排除於版本控制。已提交的金鑰難以撤銷,且往往會比示範本身存活更久。

結論

後量子遷移的高成本部分在於憑證、政策與驗證器。程式碼方面,至少對於 .NET 的 Word 文件而言,只是使用不同的 PFX 並呼叫相同的 DigitalSignOptions。將範例複製下來,指向你自己的合約,即可在數分鐘內得到三個簽署檔案、兩個驗證結果與大小比較——這比單純的估算提供更好的遷移計畫基礎。

其他資源