GitHub API OpenPGP 密钥格式

GitHub API OpenPGP key format

GitHub REST API v3 for GPG Keys 返回的 public_key 字段的格式是什么?

例如命令curl -v -H "Accept: application/vnd.github.cryptographer-preview" https://api.github.com/users/DurandA/gpg_keysreturns如下键:

pub   dsa2048/403094DF 2017-09-03 [SC] [expires: 2018-09-03]
uid         [ultimate] Arnaud Durand <arnaud.durand@unifr.ch>
sub   elg2048/A454F414 2017-09-03 [E] [expires: 2018-09-03]

根据 API doc:

The data returned in the public_key response field is not a GPG formatted key. When a user uploads a GPG key, it is parsed and the cryptographic public key is extracted and stored. This cryptographic key is what is returned by the APIs on this page. This key is not suitable to be used directly by programs like GPG.

是否可以从 CLI 或以编程方式使用这些密钥?

返回的密钥是一个裸密钥(RSA、DSA、...),如果没有 "wrapping" 它再次出现在正确的 OpenPGP 密钥数据包中,OpenPGP 的实现将无法使用它。我不建议这样做,为什么你应该能够再次构建 key 数据包,你将没有机会为子密钥和用户 ID 构建绑定签名(这需要访问私钥)并且不会成功并为此构建有用的东西。

社区中共享密钥的 "OpenPGP model" 正在从密钥服务器网络获取当前副本(包括所有当前认证和撤销),而不是依赖 "third-party-locations" 中可能过时的版本,例如 GitHub。这可以通过指纹和密钥 ID(或多或少是唯一的,见下文)解决特定密钥——不要搜索邮件地址,每个人都可以创建具有任意用户 ID 的密钥,密钥服务器不执行 any验证。

相反,再看一下 API 输出,其中 returns keyid 个对象用于所有键(一些用于子键):

[
  {
    "id": 3,
    "primary_key_id": null,
    "key_id": "3262EFF25BA0D270",
    "public_key": "xsBNBFayYZ...",
    "emails": [
      {
        "email": "mastahyeti@users.noreply.github.com",
        "verified": true
      }
    ],
    [snip]
  }
]

要使用这样的密钥 ID,运行gpg --recv-keys <key-id>。并留下 GitHub 注释以遵循最佳做法并包括完整的指纹:

这些 64 位十六进制值(本例中的 3262EFF25BA0D270)是长密钥 ID。虽然对键的任何编程引用都应该 always include the key's fingerprint, not abbreviated key IDs, at least they do not provide short key IDs that heavily suffer under collision attacks.

在撰写本文时,public_key 字段中的内容是 base64 编码的 OpenPGP 数据包,RFC 4880. gpgpdump 中定义的内容对于检查它们很有用。例如,

$ curl -s https://api.github.com/users/DurandA/gpg_keys | jq -r '.[0].public_key' | base64 -d | ./gpgpdump
Public-Key Packet (tag 6) (814 bytes)
        Version: 4 (current)
        Public key creation time: 2017-09-04T06:53:50+08:00
                59 ac 87 fe
        Public-key Algorithm: DSA (Digital Signature Algorithm) (pub 17)
        DSA p (2048 bits)
        DSA q (q is a prime divisor of p-1) (256 bits)
        DSA g (2046 bits)
        DSA y (= g^x mod p where x is secret) (2047 bits)

由于OpenPGP密钥是由一系列OpenPGP数据包组成的,理论上可以重构密钥来验证内容。为此,需要额外的用户 ID 数据包和 GnuPG 补丁。下面的Python3脚本可以用来生成用户ID包:

TAG_UID = 13

uid = 'foo@example.com'
# RFC 4880, Sec 4.2.1.  Old Format Packet Lengths
header = bytes([0x80 | (TAG_UID << 2), len(uid)])
packet = header + uid.encode('ascii')

sys.stdout.buffer.write(packet)

即使没有签名,以下 GnuPG 补丁也会强制验证。

diff --git a/g10/sig-check.c b/g10/sig-check.c
index 4c172d692..eb4653535 100644
--- a/g10/sig-check.c
+++ b/g10/sig-check.c
@@ -177,7 +177,7 @@ check_signature2 (ctrl_t ctrl,
                  gnupg_compliance_option_string (opt.compliance));
       rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
     }
-  else if (!pk->flags.valid)
+  else if (0)
     {
       /* You cannot have a good sig from an invalid key.  */
       rc = gpg_error (GPG_ERR_BAD_PUBKEY);

总之,没有自签名,验证结果不可信