💡 完整可运行示例已在 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,不需要其他任何东西。只有当签名与内容匹配且证书的序列号和指纹匹配时结果才为有效,因此使用不同方的证书进行验证会失败——示例通过两次验证(一次使用正确证书,一次使用他人证书)来证明这一点。

并排对比:预期 vs. 实际

迁移计划假设的内容 26.9 实际需要的内容
代码更改 为签名编写抽象层 使用不同的 PFX 路径
API 表面 新的后量子方法 DigitalSignOptions,保持不变
级别选择 库配置 加载哪个证书
验证 为接收方提供新工具 签名者的公用 .cer
平台工作 按操作系统处理密钥 无需额外工作——库内部自行回退
格式覆盖 所有格式 目前仅支持 Word 格式

最后一行是限制规划的关键,也正是本文诚实部分的来源。

仍未实现的功能

两项限制,了解后再作承诺很重要。

  • 格式覆盖:在 26.9 中仅支持 Word(DOCX、DOC、ODT 以及 Word 系列的其他格式)。PDF、电子表格和演示文稿无法使用 ML-DSA 签名。对于以 PDF 为主的流水线,此版本更适合原型验证和测量,而非迁移。
  • 验证器支持:目前尚无针对 ML-DSA 的标准 XML‑DSig 标识符,因此 Microsoft Word 可能不会将签名标记为有效,即使它在密码学上是正确的并且通过 API 能正确验证。这是标准缺口而非缺陷,意味着验证应在代码中完成,而不是依赖审阅者打开文件后查看横幅。

还有一个平台细节无需任何操作:.NET 并非在所有环境都能读取 ML-DSA 密钥,包括 .NET 8 下的 Linux。若无法读取,GroupDocs.Signature 会通过 Word 引擎读取证书,因此同一构建既可在开发者笔记本上运行,也可在 Linux 容器中运行,无需条件编译。

鉴于这些限制,现在值得去做吗?

是,原因有二,且与代码本身无关。证书采购周期长——公共 CA 仍在逐步推出 ML-DSA 发行——因此组织层面的工作越早开始越有利。而“我们今天能否生成后量子签名”已经成为合规团队开始关注的问题;能够用已签名的文档而非仅有计划来回答,值得花费一个下午的时间。

示例实际证明了什么

四个方法按顺序执行,退出码与结果关联。它使用 ML-DSA-65 对合同进行签名并打印所用证书的主题;它在三个级别上分别签名同一合同并打印产生的文件大小;它对已签名文件进行两次验证——一次使用签名者的公钥证书(预期成功),一次使用其他签名者的证书(预期失败)。随后列出输出文件中发现的所有数字签名。

第二次验证是最值得复制的部分。仅展示了有效输入的例程并不能说明它会否拒绝无效输入,而这正是签名验证的核心问题。

真实案例:三十年合同

长期保存的档案正是此类技术从理论走向实践的场景。今天签署并保存三十年的合同必须在这段时间内保持可验证,无论密码学如何演进,而“先收集、后解密”正是针对这类材料的已记录威胁模型。

针对这种档案,实际的当务之急是双轨并行:对 ML-DSA 尚未覆盖的格式继续使用 RSA,对 Word 输出使用 ML-DSA-65 或 ML-DSA-87,并记录每份文档使用的算法,以便未来审计时无需打开文件即可区分。

在复制示例前需要修复的一件事

仓库中提供了自签名的 ML-DSA 证书,使示例能够开箱即用,这意味着 documents/ 目录下有四个 PFX 文件和一个硬编码密码。对于仅在示例内部有效的临时测试证书来说,这没问题。

但这不是应当迁移到自己仓库的做法。应改为在运行时生成测试证书,类似 GroupDocs 的 certificate-validity 示例,或将证书完全排除在版本控制之外。提交的密钥既难以撤销,又往往会比演示本身存活更久。

结论

后量子迁移的高成本部分在于证书、政策和验证器。代码层面——至少在 .NET 环境下处理 Word 文档时——只需使用不同的 PFX 并调用相同的 DigitalSignOptions。克隆示例,指向你自己的合同,即可在几分钟内得到三份已签名文件、两条验证结果以及大小对比,这比单纯的估算为迁移计划提供了更可靠的依据。

其他资源