繁体   English   中英

验证签名的 git 提交?

[英]Verifying signed git commits?

使用较新版本的git ,可以使用 PGP 密钥对单个提交(除了标签)进行签名:

git commit -m "some message" -S

您可以--show-signature选项在git log的输出中显示这些签名:

$ git log --show-signature
commit 93bd0a7529ef347f8dbca7efde43f7e99ab89515
gpg: Signature made Fri 28 Jun 2013 02:28:41 PM EDT using RSA key ID AC1964A8
gpg: Good signature from "Lars Kellogg-Stedman <lars@seas.harvard.edu>"
Author: Lars Kellogg-Stedman <lars@seas.harvard.edu>
Date:   Fri Jun 28 14:28:41 2013 -0400

    this is a test

但是有没有办法以编程方式验证给定提交上的签名,而不是通过 grepping git log的输出? 我正在寻找与git tag -v等效的提交——它将提供退出代码,指示给定提交是否存在有效签名。

以防万一有人像我一样通过搜索引擎访问此页面:自问题发布以来的两年内已经提供了新工具:现在有用于此任务的 git 命令: git verify-commitgit verify-tag可分别用于验证提交和标签。

注意:在 git 2.5 之前, git verify-commitgit verify-tag只显示人类可读的消息。
如果你想自动化检查,git 2.6+(2015 年第三季度)添加了另一个输出。

请参阅brian m 的commit e18443ecommit aeff29dcommit ca194d5commit 434060ecommit 8e98e5fcommit a4cc18fcommit d66aeff (2015 年 6 月 21 日) 卡尔森 ( bk2204 )
(由Junio C gitster合并-- gitster -- in commit ba12cb2 ,2015 年 8 月 3 日)

verify-tag / verify-commit : 添加打印原始 gpg 状态信息的选项

verify-tag / verify-commit默认在标准错误上显示人类可读的输出。
但是,访问原始 gpg 状态信息也很有用,该信息是机器可读的,允许自动实施签名策略

添加一个--raw选项,使verify-tag生成关于标准错误的 gpg 状态信息,而不是人类可读的格式。

加:

如果签名良好但密钥不受信任,则verify-tag成功退出。 verify-commit未成功退出。
这种行为上的分歧是出乎意料和不受欢迎的。
由于verify-tag较早存在,添加一个失败的测试让verify-commit共享verify-tag的行为。


git 2.9(2016 年 6 月)更新git merge 文档

请参阅Keller Fuchs (``) 的提交 05a5869 (2016 年 5 月 13 日
帮助者: Junio C gitster ( gitster )
(由Junio C gitster合并-- gitster -- in commit be6ec17 ,2016 年 5 月 17 日)

--verify-signatures:
--no-verify-signatures:

验证正在合并的侧分支的提示提交是否使用有效密钥签名,即具有有效 uid 的密钥:在默认信任模型中,这意味着签名密钥已由受信任的密钥签名。
如果侧分支的提示提交未使用有效密钥签名,则合并中止


更新 Git 2.10(2016 年第三季度)

提交b624a3e通过(2016年8月16日) Linus Torvalds的( torvalds
(由Junio C gitster合并gitster 提交 83d9eb0,2016年 8 月 19 日)

gpg-interface :在验证 pgp 签名时更喜欢“长”密钥格式输出

git log --show-signature ”和其他显示PGP签名验证状态的命令现在显示更长的key-id,因为32位key-id是上世纪的。

Linus 的原始版本被重新定位以适用于维护轨道,以防万一过去陷入困境的二进制分发者想要将其带到他们的旧代码库中。


Git 2.11+(2016 年第四季度)甚至会更加精确。

请参阅Michael J Gruber ( mjg ) 的commit 661a180 (2016 年 10 月 12 日
(由Junio C gitster合并gitster 提交 56d268b ,2016 年 10 月 26 日)

%G? ”中显示的 GPG 验证状态不够丰富,无法区分由过期密钥生成的签名、由已撤销密钥生成的签名等。
已分配新的输出字母来表达它们

根据gpg2 的doc/DETAILS

对于每个签名,只会GOODSIGBADSIGEXPSIGEXPKEYSIGREVKEYSIGERRSIG代码GOODSIG

git pretty-format文档现在包括:

  • ' %G? ':显示
  • G ”表示良好(有效)签名,
  • B ”表示签名错误,
  • U ”表示有效性未知的良好签名,
  • X ”表示已过期的好签名,
  • Y ”表示由过期密钥生成的良好签名,
  • R ”表示由已撤销的密钥生成的良好签名,
  • E ”如果无法检查签名(例如缺少密钥)和“ N ”没有签名

Git 2.12(2017 年第一季度)“ git tag ”和“ git verify-tag学会了将 GPG 验证状态放在它们的“ --format=<placeholders> ”输出格式中

请参阅圣地亚哥·托雷斯 ( SantiagoTorres ) 的提交 4fea72f提交 02c5433提交 ff3c8c8 (2017 年 1 月 17 日
请参阅Lukas Puehringer (``) 的commit 07d347ccommit 2111aa7commit 94240b9 (2017 年 1 月 17 日
(由Junio C gitster合并-- gitster -- in commit 237bdd9 ,2017 年 1 月 31 日)

--format添加到git tag -v会使 GPG 验证的默认输出静音,而是打印格式化的标签对象。
这允许调用者在 GPG 验证时交叉检查来自 refs/tags 的标记名与来自标记对象标头的标记名。


Git 2.16(2018 年第一季度)将允许提交签名验证更加自动化,使用merge.verifySignatures配置变量。

请参阅Hans Jerry Illikainen (``) 的commit 7f8ca20commit ca779e8 (2017 年 12 月 10 日
(由Junio C gitster合并-- gitster -- in commit 0433d53 ,2017 年 12 月 28 日)

merge :为verifySignatures添加配置选项

git merge --verify-signatures可用于验证被合并的分支的提示提交是否正确签名,但每次都必须指定它很麻烦。

添加一个默认启用此行为的配置选项,它可以被--no-verify-signatures覆盖。

git merge config 手册页现在显示:

 merge.verifySignatures:

如果为 true,则这等效于--verify-signatures命令行选项。


Git 2.19(2018 年第三季度)更加有用,因为“ git verify-tag ”和“ git verify-commit ”已被教导使用底层“ gpg --verify ”的退出状态来表示他们发现的错误或不受信任的签名。

注意:在 Git 2.19 中, gpg.format可以设置为“ openpgp ”或“ x509 ”, gpg.<format>.program用于指定使用什么程序来处理格式)允许 x.509通过“ gpgsm ”使用 CMS 的证书,而不是通过“ gnupg ”使用openpgp

请参阅Junio C gitster ( gitster ) 的commit 4e5dc9c (2018 年 8 月 9 日
帮助者: Vojtech Myslivec ( VojtechMyslivec )brian m。 卡尔森 ( bk2204 )杰夫金 ( peff )
(由Junio C gitster合并-- gitster -- in commit 4d34122 ,2018 年 8 月 20 日)

gpg-interface : 将退出状态从gpg传播回调用者

当 gpg-interface API 在 2015 年年中 v2.6.0-rc0~114 左右统一​​支持签名标签和签名提交的签名验证代码路径时,我们不小心放松了 GPG 签名验证。

在此更改之前,签名提交通过从 GPG 寻找“ G ” ood 签名来验证,同时忽略“ gpg --verify ”进程的退出状态,而签名标签通过简单地传递"gpg --verify的退出状态来验证“通过。

我们目前拥有的统一代码会忽略“ gpg --verify ”的退出状态,并在签名与未过期的密钥匹配时返回成功验证,而不管对密钥的信任度如何(即除了“ G ”好,我们接受“ U “受信任的人)。

当底层“ gpg --verify ”(或“ gpg.program ”配置变量指定的自定义命令)执行此操作时,使这些命令以其退出状态发出失败信号。
这本质上以一种向后不兼容的方式改变了他们的行为,以拒绝使用不受信任的密钥制作的签名,即使他们正确验证,因为这就是“ gpg --verify ”的行为方式。

请注意,如果输出gpg.program签名良好或计算正确但使用不受信任的密钥制作,则代码仍会覆盖从“ gpg ”(或gpg.program )获得的零退出状态,以捕获围绕“ gpg ”编写的糟糕包装用户可能会给我们。

我们可以从这个回退代码中排除“ U ”信任支持,但这会在一次提交中进行两个向后不兼容的更改,所以现在让我们避免这种情况。
如果需要,可以进行后续更改。


在进行任何加密之前,需要信任/签署密钥

在信任方面,有进展:
在 Git 2.26(2020 年第一季度)中,引入了gpg.minTrustLevel配置变量来告诉各种签名验证代码路径所需的最低信任级别。

请参阅Hans Jerry Illikainen ( illikainen ) 的commit 54887b4 (27 Dec 2019 )
(由Junio C gitster -- gitster --commit 11ad30b 中合并,2020 年 1 月 30 日)

gpg-interface : 添加 minTrustLevel 作为配置选项

签字人:Hans Jerry Illikainen

以前,合并和拉取操作的签名验证会检查密钥在verify_merge_signature()中是否具有TRUST_NEVERTRUST_UNDEFINED的信任级别。

如果是这种情况,进程die() 'd。

其他进行签名验证的代码路径完全依赖于check_commit_signature()的返回码。

check_commit_signature()认为使用好的密钥制作的签名, check_commit_signature()其信任级别如何,都被认为是有效的。

这种行为上的差异可能会导致用户错误地认为 Git 始终会考虑其密钥环中密钥的信任级别,即使对于不考虑的操作(例如在verify-commitverify-tag

它的工作方式是通过gpg-interface.c将密钥/签名状态的结果最低的两个信任级别存储在signature_check结构的result成员中(遇到的这些状态行中的最后一个被写入result ) .

这些分别记录在 GPG 小节General status codesKey related

GPG 文档对TRUST_ status代码有以下说明:


这些是几个类似的状态代码:

 - TRUST_UNDEFINED <error_token> - TRUST_NEVER <error_token> - TRUST_MARGINAL [0 [<validation_model>]] - TRUST_FULLY [0 [<validation_model>]] - TRUST_ULTIMATE [0 [<validation_model>]]

对于好的签名,发出这些状态行之一以指示用于创建签名的密钥的有效性。
错误标记值目前仅由 gpgsm 发出。


我的解释是信任级别在概念上不同于密钥和/或签名的有效性。

这似乎也是check_signature()中旧代码的假设,其中“ G ”(如在GOODSIG )和“ U ”(如在TRUST_NEVERTRUST_UNDEFINED)的结果都被认为是成功的。

在两种情况下,“的结果U ”具有特殊的含义是在verify_merge_signature()其中,这造成gitdie()和在format_commit_one()其中,它影响的输出%G?格式说明)。

我认为重构TRUST_ status行的处理是TRUST_ status这样用户可以配置全局强制执行的最低信任级别,而不是让git各个部分(例如合并)自己做(具有向后兼容性的宽限期除外) )。

我还认为不将信任级别存储在与密钥/签名状态相同的结构成员中是有意义的。

虽然TRUST_ status代码的存在确实意味着签名是好的(请参阅上面包含的代码段中的第一段),但据我所知,来自 GPG 的状态行的顺序没有明确定义; 因此,如果密钥/签名状态存储在signature_check结构的同一成员中,那么信任级别可能会被密钥/签名状态覆盖,这似乎是合理的。

这个补丁引入了一个新的配置选项: gpg.minTrustLevel

它将信任级别验证整合到gpg-interface.c并添加一个新的trust_level成员到signature_check结构。

通过在verify_merge_signature()引入一个特殊情况来保持向后兼容性,这样如果没有设置用户可配置的gpg.minTrustLevel ,则拒绝TRUST_UNDEFINEDTRUST_NEVER的旧行为将被强制执行。

另一方面, gpg.minTrustLevel设置了gpg.minTrustLevel ,则该值将覆盖旧行为。

同样, %G? 对于使用信任级别为TRUST_UNDEFINEDTRUST_NEVER,的密钥进行的签名,格式说明符将继续显示“ UTRUST_NEVER,即使“ U ”字符不再存在于signature_check结构的result成员中。

还为想要显示签名的所有可能信任级别的用户引入了新的格式说明符%GT

另一种方法是简单地删除verify_merge_signature()的信任级别要求。

这也会使行为与执行签名验证的 git 其他部分保持一致。

但是,需要最低信任级别来签署密钥似乎确实有实际用例。

例如,Qubes OS 项目使用的构建系统当前解析来自 verify-tag 的原始输出,以便为用于签署 git tags 的密钥断言最低信任级别。

git config gpg手册页现在包括:

gpg.minTrustLevel:

指定签名验证的最低信任级别。
如果未设置此选项,则合并操作的签名验证需要至少具有marginal信任度的密钥。
执行签名验证的其他操作需要至少具有undefined信任的密钥。
设置此选项会覆盖所有操作所需的信任级别。 支持的值,按重要性升序排列:

  • undefined
  • never
  • marginal
  • fully
  • ultimate

Git 2.26 (Q1 2020) 中,“ git show ”和其他人在其错误输出中以原始格式给出了一个对象名称,该名称已被更正为以十六进制给出。

show_one_mergetag:以十六进制形式打印非父项。

当合并标签命名一个非父对象时,这可能发生在一个浅克隆之后,它的哈希值以前被打印为原始数据。
改为以十六进制形式打印它。

git clone --depth 1 --no-local . shallow之后使用git -C shallow log --graph --show-signature -n1 plain-shallow进行git clone --depth 1 --no-local . shallow git clone --depth 1 --no-local . shallow


在 Git 2.27(2020 年第二季度)中,与 GnuPG 交互的代码已被重构。

提交 6794898提交 f1e3df3 (04 Mar 2020) by Hans Jerry Illikainen ( illikainen )
(由Junio C gitster合并gitsterfa82be9 提交中,2020 年 3 月 27 日)

gpg-interface : 更喜欢check_signature()进行 GPG 验证

签字人:Hans Jerry Illikainen

此提交refactors使用verify_signed_buffer()以外gpg-interface.c到使用check_signature()来代替。

它还将verify_signed_buffer()转换为文件本地函数,因为它现在仅由check_signature()内部调用。

以前在 Git 的不同部分使用了两个全局作用域函数来执行 GPG 签名验证: verify_signed_buffer()check_signature()

现在只使用check_signature()

verify_signed_buffer()函数不能防止Michał Górny 所描述的重复签名

相反,它仅确保来自 GPG 的GOODSIG退出代码和至少一个GOODSIG状态字段的存在。

这与check_signature()形成对比,如果遇到多个签名,则返回错误。

如果调用者不自己解析和验证 GPG 状态消息的各个部分,那么较低的验证程度会使verify_signed_buffer()的使用出现问题。

并且处理这些消息似乎是一项应该保留给gpg-interface.c使用函数check_signature()

此外,使用verify_signed_buffer()使得引入依赖 GPG 状态行内容的新功能变得困难。

现在,所有进行签名验证的操作都共享一个gpg-interface.c入口点。

这使得将 GPG 签名验证中更改的或附加的功能传播到 Git 的所有部分变得更加容易,而不会出现不执行相同程度验证的奇怪边缘情况


在 Git 2.31(2021 年第一季度)中,签名提交和标签现在允许验证对象,其两个对象名称(一个在 SHA-1 中,另一个在 SHA-256 中)都已签名。

请参阅brian m 的commit 9b27b49commit 88bce0ecommit 937032ecommit 482c119 (2021 年 2 月 11 日)和commit 1fb5cf0commit 83dff3e (2021 年 1 月 18 日) 卡尔森 ( bk2204 )
(由Junio C gitster合并-- gitster -- in commit 15af6e6 ,2021 年 2 月 22 日)

commit :在解析签名提交时忽略其他签名

签字人:brian m. 卡尔森

当我们创建具有多个签名的提交时,这些签名都不包含另一个。
因此,当我们生成已签名的有效负载以验证提交时,我们必须剥离任何其他签名,否则有效负载将与签名的不同。
这样做,并为使用多种算法进行验证做准备,将我们要验证的算法传递给parse_signed_commit

对代码的粗略检查表明没有这种直接的方法。

git 源中的所有测试都依赖于grep ping git show的输出(有关测试,请参阅t/t7510-signed-commit.sh )。

您可以使用--pretty "%H %G?%"类的--pretty "%H %G?%"自定义输出,使其易于解析。

看来您可以要求git merge来验证签名,但同样,它的测试依赖于grep (请参阅t/t7612-merge-verify-signatures.sh )。 看起来无效的签名确实会导致git merge以错误的签名退出,因此您今天可能会通过在某处进行测试合并并抛出该合并来解决此问题,但这似乎比仅调用 grep 更糟糕。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM