如何使用RFC3161(可信)时间戳证明我的Git存储库中提交的年龄?

20

更新

我已经在StackExchange Code Review网站上发布了一个脚本,我正在使用它。

我的原始问题是是否有一种方法可以使用X.509证书和时间戳签署Git提交?有一段时间,我认为只有受信任的第三方才能为我签署的使用X.509证书签名的内容加上时间戳。这并不是事实。用X.509证书进行数字签名和信任时间戳是互相排斥的。我已经更新了我的问题以反映这一点。

正如VonC指出的,使用X.509证书对Git提交进行签名并没有任何价值。使用GPG密钥是更好的选择,因为Git具有内置支持。

我接受了Greg的答案,因为它最接近我所要求的,尽管我的原始问题有点模糊。正如Greg所指出的那样,如果你能证明你在某个时间点知道一个提交哈希值,那么就可以保证你在那个时间知道哈希值对应的存储库内容,并且不需要在存储库中存储任何额外的数据。时间戳数据可以存储在任何地方。

可以使用openssl(v1.0.0+)和curl请求RFC3161时间戳来获取提交哈希值的时间戳。

请求时间戳

您需要一些信息:

  • URL - RFC3161时间戳服务
  • REV - 您要获取时间戳的版本(哈希)。必须是完整的哈希。
CONTENT_TYPE="Content-Type: application/timestamp-query"
ACCEPT_TYPE="Accept: application/timestamp-reply"

openssl ts -query -cert -digest "$REV" -sha1 \
    | curl -s -H "$CONTENT_TYPE" -H "$ACCEPT_TYPE" --data-binary @- $URL

上述代码将有符号时间戳输出到 stdout。如果时间戳服务拒绝请求,它也可能输出错误消息。

验证时间戳

这与请求时间戳非常相似,但您还需要:

  • CAFILE - 时间戳服务的证书链,返回到根CA

时间戳服务应该使用由受信任机构颁发的证书对时间戳进行签名。如果没有,您的时间戳就没有太多可信度。如果无法找到或创建正确的证书链,请尝试使用由 curl 发布的 cacert.pem。它在这里

下面的代码片段假设一个现有的、已签名的时间戳响应被传递给了 stdin。应该可以直接将上面的请求导入到下面的验证命令中。如果将响应存储在变量中,则可能需要对其进行 base64 编码/解码 (man base64)。

openssl ts -verify -digest "$REV" -in /dev/stdin -CAfile "$CAFILE"

如果您检查回复,您会注意到请求摘要与使用的Git修订版本相匹配。您可以使用此命令检查回复的纯文本版本。

openssl ts -reply -in /dev/stdin -text

这是一个回复的示例,我在顶部添加了Git版本。

--------------------------------------------------------------------------------
Revision: 871d715e5c072b1fbfacecc986f678214fa0b585
--------------------------------------------------------------------------------
Status info:
Status: Granted.
Status description: unspecified
Failure info: unspecified

TST info:
Version: 1
Policy OID: 1.3.6.1.4.1.6449.2.1.1
Hash Algorithm: sha1
Message data:
    0000 - 87 1d 71 5e 5c 07 2b 1f-bf ac ec c9 86 f6 78 21   ..q^\.+.......x!
    0010 - 4f a0 b5 85                                       O...
Serial number: 0xB2EA9485C1AFF55C6FFEDC0491F257C8393DB5DC
Time stamp: Aug 15 08:41:48 2012 GMT
Accuracy: unspecified
Ordering: no
Nonce: 0x615F0BF6FCBBFE23
TSA: DirName:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Time Stamping Signer
Extensions:

其他注意事项

许多时间戳服务要求用户在脚本签名请求中添加延迟。确保您了解所使用的服务是否需要。目前我使用的Comodo要求在脚本请求之间添加15秒延迟。我选择使用Comodo只是因为我从他们那里购买了代码签名证书。

Git笔记似乎是存储签名时间戳响应的明显选择,但我还没有完整的解决方案可以发布。我目前被卡在这个问题上。

下面是我的原始问题和更新内容。


我想要证明我进行的Git提交的时间,并证明我的存储库的历史记录没有被重写。它不必每次提交都这样做,每天或每周一次就足够了。是否有推荐的方法来实现这一点?

我知道我可以使用GPG密钥签署Git提交,但我想知道是否有一种方法可以使用X.509证书对我的提交进行签名,同时利用在线时间戳服务,例如 http://timestamp.comodoca.com/rfc3161

如果没有,是否会每天使用git rev-parse --verify HEAD将当前修订版本转储到文本文件中,签署该文件,并提交,足以证明(大约)我的代码何时编写?

补充信息以便更清晰理解

我知道Git保证存储库的完整性,但据我所知,如果我控制存储库,则第三方必须相信我没有重写存储库的历史记录或将我的时钟回滚并创建一个完全虚假的存储库,以“证明”我的代码比它实际上更旧?我也不想公开发布我的存储库。

以下是一个虚构的用例,应该能够更好地说明我想要做什么。

我在网上发布了一些代码。一年后,有人将相同的代码复制并发布到书籍或文章中,并声称我是复制他们的人。此时,我想能够拿出我的存储库并证明我一年前就提交了那段代码,而他们是重新发布它。

通过使用带有时间戳服务的X.509证书,我可以证明签名发生的时间。只要我能证明我知道一年前提交的哈希值,Git就保证了归档的完整性。

或者,是否有一种使用GPG密钥签名提交,但具有经过验证的时间戳的方法?是否有一个受信任的第三方提供类似X.509证书可用的时间戳服务,但用于GPG?

也许我可以结合使用GPG密钥和X.509证书。假设我在存储库中保留了我的(公共)GPG密钥的副本,如果我每天结束时都执行以下操作,是否有效?

  1. 使用我的X.509证书和在线时间戳服务对我的(公共)GPG密钥进行签名。
  2. 使用我的(私有)GPG密钥对更改进行签名,并将其提交到存储库。

有什么意义呢?只要没有人在你的本地仓库上搞乱,历史的完整性就得到了保证。即使你从重写历史的人那里拉取更改,这些更改也不会自动合并,两份历史副本都会保留在仓库中。如果你想要一个没有重写历史的确定性仓库副本,你可以让每个人将他们的工作推送到一个拒绝强制推送的服务器上,或者类似的方式... - Robert Rouhani
1
我希望能够向他人证明我没有重写存储库的历史记录。 - Ryan J
2
只需允许他们定期拉取您的存储库。如果您重写历史记录,sha1将更改,他们会知道的。 - kan
@kan 有些人可能希望在将来的某个时间向当局证明提交存在于某个时间点,并且没有被后来重写 - 例如在审计期间。因此,“允许他们定期拉取您的存储库”不是一个解决方案。为提交哈希存储受信任的时间戳令牌似乎是正确的方法,问题是最优雅的方法是什么。 - matthias_buehlmann
如果我们信任审计员,那么他们有责任拉取并跟踪已经拉取的内容。如果没有,那么TSA会提供帮助。 - kan
@kan 这些是未来的审计员 - 甚至在您创建提交时可能还不存在。 - matthias_buehlmann
5个回答

7

您只需要公开发布SHA1(提交ID)即可。如果您愿意,您可以使用X.509证书(使用适当的时间戳服务)对该SHA1进行签名并保留它。如果有人质疑您的作者身份,您可以轻松地展示您知道在生成该特定SHA1的特定时间点上存储库的内容。您不需要实际存储任何签名代码存储库中。


这真的取决于想要证明给哪个实体,因为该实体可能已经指定了必须使用特定 TSA 服务对数据进行时间戳。如果是这种情况,则可以通过请求 RFC3161 令牌来对 SHA1 提交哈希进行时间戳(无需使用 X.509 证书进行额外签名,时间戳由可信任的 TSA 证书签名)。然而,还需要检查所采用的策略是否接受 SHA1 作为安全散列算法进行证明。如果不是,则不能依赖提交哈希(或使用 --object-type=sha256 存储库)。 - matthias_buehlmann

3
只需在最新提交中添加时间戳证书。sha1将验证证书未被修改,证书本身将验证所有声明的“事实”,例如来自可信服务器的日期和时间戳以及您声称自己是谁等等。也就是说,提交会签署证书,如VonC从Linus的演讲中引用的那样;-)
[编辑]显然,您需要发布该新提交的sha1,否则您可以修改它(和证书),并使用新sha1来声明该新提交历史记录中的任何内容。与往常一样,这是创建相互信任的网络。

如果时间戳已经存在(同时使用证书签名也不是必须的),则无需发布哈希。可信时间戳向任何信任 TSA 的人证明哈希(因此提交及其所有数据和历史记录)在时间戳认证的时间存在。 - matthias_buehlmann
@matthias_buehlmann 为了澄清,关键是选择两个“哈希”(提交或时间戳)中的哪一个作为参考项,以及哪一个是包含在参考项内部的次要内容。时间戳可以证明提交,或者提交可以证明时间戳。然后您可以“发布”引用,以便其他人知道并信任它们。 - Philip Oakley

2
我写了一篇文章,详细介绍了如何使用RFC3161时间戳对git存储库进行时间戳标记,并提供了一个后提交挂钩,可以自动完成此操作:

https://matthias-buehlmann.medium.com/git-as-cryptographically-tamperproof-file-archive-using-chained-rfc3161-timestamps-ad15836b883

以下是关键点:

  1. SHA1 算法现在可能不再安全,特别是如果它要在未来几年内证明某些事情 (https://sha-mbles.github.io/)。因此,建议使用 sha256 哈希值初始化 git 存储库 git init --object-type=sha256(即使 sha256 支持仍被视为实验性)。

  2. 将时间戳(和 LTV 数据)与存储库一起存储有益处 - 特别是为了任意延长时间戳令牌的寿命,并通过向存储库添加先前时间戳的 CRL 数据并使用新时间戳对其进行时间戳处理,以保护它们免受泄漏私钥而变得无效。

  3. Git 非常适合使用 RFC3161 进行时间戳处理,这为所有数据更改提供了防篡改的证据记录。


1

我不确定我理解了,因为Linus创建Git时就有这个特定功能(即你输入的内容的完整性与输出的内容是完全相同的)。

请参见2007年在Google的演讲文字稿):

大多数我甚至不需要尝试就可以丢弃它们。
如果你没有分布式,那么你就不值得使用,就是这么简单。
如果你表现不佳,那么你就不值得使用,就是这么简单。
而且,如果你不能保证我放入SCM的东西完全相同地出来,那么你就不值得使用。
坦白地说,这几乎解决了所有问题。
有很多SCM系统不能保证你再次获得的东西与你放入的东西相同。如果你有内存损坏,如果你有磁盘损坏,你可能永远不会知道。唯一的方法是在检出文件时注意到文件中存在损坏。源代码控制管理系统根本无法保护你。这甚至不是罕见的情况。它非常普遍。

所以我认为添加另一个完整性功能不会增加任何价值。
而且“时间戳”也不是一个好主意,因为它们首先并没有被记录在DVCS中(参见“Git:检查旧文件和原始创建/修改时间戳”,以及“Git的use-commit-times等效项是什么?”)。


@RyanJ,任何新提交所关联的“作者日期”不就是为此而设吗?无需新的时间戳:提交已经包含了您需要的所有信息。 - VonC
2
那个时间戳没有经过第三方验证,对吧?我猜它是本地生成的,所以它基于我的话,而不是可证明的东西。使用来自Comodo、Verisign等的时间戳意味着第三方已经保证了它的准确性。当我用我的X.509证书签名和时间戳某些东西时,证书颁发机构的时间戳服务会显示为计数签名。例如,当我从http://timestamp.comodoca.com/authenticode请求时间戳时,计数签名来自“COMODO Time Stamping Signer”。 - Ryan J
@RyanJ 一个提交哈希代表着它本身、它的内容和所有祖先的完整性。如果我们谈论的是仓库根目录的提交,那么单个提交SHA1代表了整个仓库的完整性。如果你在提交中添加了额外的数据(在注释中),你将无法更改这些数据而不更改SHA1。你也可以稍后使用git notes(http://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html)添加该信息,但它有自己独立的SHA1来记忆(即根提交的SHA1已经不够用了)。 - VonC
这应该是我最后一个问题了。我感谢您的帮助,我几乎理解了,但我不太明白您所说的存储库根目录的提交是什么意思。如果我使用 git checkout --orphan MYBRANCH 创建一个孤立的分支,那么 MYBRANCH 上的提交哈希值是否仍然可以验证其他分支的完整性?我一直在尝试阅读这个(http://git-scm.com/book/ch9-2.html),但是有点难以理解。 - Ryan J
@RyanJ 是的,孤立分支会引入另一个SHA1,独立于根树SHA1(即使https://dev59.com/RXM_5IYBdhLWcg3waiiJ建议创建一个新的repo ;))。 - VonC
显示剩余4条评论

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接