如何通过密码学手段证明某个文件在特定时间点确实存在?
我需要证明在某个时间点存在一个特定文件(例如PDF合同或构建产物),且该证明在锚定后能够独立可验证,并且不需要验证方信任发行平台。
要求: - 证明必须与文件的内容绑定,而非元数据 - 必须能经受发行平台的变动(无厂商锁定) - 第三方无需账户即可验证
我可以在Python中计算SHA-256哈希值:
import hashlib
with open("contract.pdf", "rb") as f:
sha256 = hashlib.sha256(f.read()).hexdigest()
print(sha256)
但仅仅一个哈希并不能证明创建时间。文件系统时间戳是可编辑的,平台时间戳是内部的。
将文件哈希锚定到一个公开可验证且不可变的时间线的标准加密方法是什么?
解决方案
许多证书机构还提供一个相邻的“时间戳机构”(Time-Stamping Authority,TSA)服务,在这种服务中,他们不是对证书进行签名,而是对某些任意数据的哈希值进行签名。你会得到一个标准的CMS(S/MIME)消息,其签名可以通过TSA的证书链进行验证。
这种时间戳服务通常用于Authenticode签名(也就是对Windows的 EXE/MSI/DLL文件进行签名)——时间戳服务的对签名(counter-signature)能防止已签名的.exe在开发者证书一年后到期时突然失效。你可以对一个.exe右键,选择“数字签名 > 详细信息”来查看包含CA的时间戳的对签名,以及签名者自述时间戳。
例如,DigiCert、Sectigo、GlobalSign提供公共TSA服务,Apple与 Microsoft也有提供——它们大多名义上用于代码签名,但其实不在意你提交的是哪种哈希值。
所有这些服务都使用RFC 3161协议,你可以使用 [python-rfc3161] 或 [OpenSSL::TimeStamp] 或 openssl ts 来生成请求(通过HTTP POST提交)以及/或验证返回的时间戳。当然,如果你的“构建产物”是一个.exe/.dll/.msi文件,那么时间戳已经内建于 signtool。
(看起来RFC 3161已经演进为ANSI ASC X9.95标准。我不知道在哪些场景会使用它。)
类似的服务也被用于eIDAS的“合格文档签名”之中的XAdES/PAdES/CAdES规范,在其中你会看到一整套规范,规定了需要将哪类辅助数据(如证书链、OCSP响应等)打包到文档中,以使其在二十年后离线也能实现完全可验证。
我不知道这在世界其他地方是否也很常见,但在我所在的欧洲地区,数字签署的合同要么以PAdES格式的签名PDF要么是放在XAdES ASiC-E档案中的普通PDF,无论哪种情形,时间戳和所有验证元数据(包括用于签名者和TSA的信息)都打包在文档中。
¤AdES格式很复杂(而且“合格”的时间戳服务通常需要付费),但它们其实已经是一个已经发明的轮子——因此如果你真的想对合同进行时间戳,强烈建议先了解是否在你所在地区的法律中已经被采纳为标准格式。
同样要记住,正如存在用于不同目的的不同“受信任CA”名单一样,也存在不同的“受信任TSA”名单,因此需要考虑未来的受众。例如,eIDAS TSL中的许多TSA并未内置在Windows的 CA/TSA存储中——反之,常用于Authenticode的 TSA也未必在eIDAS TSL中。(而像比特币区块链时间戳这样的替代方法则两者都不在。)最终,无论采用哪种方法,你都需要让验证方相信你所选择的TSA(或区块链等)确实是被信任用于时间戳的。
(在其他一些不太标准的变体中,似乎长期存在一个提供PGP基于时间戳的服务。十年前,在区块链热潮之前,曾有一家名为Guardtime的公司专门从事时间戳服务——他们每月不是发放CA证书,而是在报纸上公布一个Merkle树根哈希,作为所有“叶子哈希”在印刷前就已存在的物理证据。)
1 Apple提供 http://timestamp.apple.com/ts01 用于对macOS软件进行签名,但其文档确实指出它并非用于除了 codesign 以外的其他用途,因此Apple的提及只是为了支持这是“标准的加密方法”的论点。