GPG 如何在給定公鑰的情況下產生 MD5 指紋?

GPG 如何在給定公鑰的情況下產生 MD5 指紋?

我將以下 GPG 公鑰儲存在名為publickey.pubASCII Armor (Radix-64) 並以 ASCII Armor (Radix-64) 編碼的檔案中:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.3ia

mQCNAzNko/QAAAEEANZ2kpN/oMkz4tqzxvKPZws/XwsD0Y+E5/y7P2DIw4uHS/4N
syQbgkdrZhPBlXDv68DQioHXWsb904qyr7iZB1LC5ItK9MgqlK+Z2mvPqsGbHM8J
+oYib8kf2zJ6HvrYrP7NYB0tN9YYum2ICtx+hIi6aKGXdB1ATA5erwYmu0N9AAUR
tClSYWxmIFMuIEVuZ2Vsc2NoYWxsIDxyc2VAZW5nZWxzY2hhbGwuY29tPokAlQMF
EDNko/QOXq8GJrtDfQEBKVoD/2K/+4pcwhxok+FkuLwC5Pnuh/1oeOYHiKYwx0Z3
p09RLvDtNldr6VD+aL9JltxdPTARzZ8M50UqoF9jMr25GifheFYhilww41OVZA3e
cLXlLgda1+t0vWs3Eg/i2b0arQQDaIq7PeRdjdEDgwnG4xBaqaAqfgxwOXJ+LPWF
hiXZ
=K7lL
-----END PGP PUBLIC KEY BLOCK-----

如果我輸入:

$ gpg --with-fingerprint publickey.pub

我得到了鑰匙的指紋:

密鑰指紋 = 00 C9 21 8E D1 AB 70 37 DD 67 A2 3A 0A 6F 8D A5

那麼,GPG 是如何做到的呢?我的意思是,是否有一個命令我可以在不使用的情況下運行gpg並且仍然獲得相同的指紋?例如,使用 SSH,給定公鑰,我可以執行以下操作:

$ cat ~/.ssh/id_rsa.pub | awk '{print $2}' | base64 -D | md5

這將傳回與以下內容相同的哈希值:

$ ssh-keygen -l -f ~/.ssh/id_rsa.pub

我知道公鑰的實際內容應該是:

mQCNAzNko/QAAAEEANZ2kpN/oMkz4tqzxvKPZws/XwsD0Y+E5/y7P2DIw4uHS/4N
syQbgkdrZhPBlXDv68DQioHXWsb904qyr7iZB1LC5ItK9MgqlK+Z2mvPqsGbHM8J
+oYib8kf2zJ6HvrYrP7NYB0tN9YYum2ICtx+hIi6aKGXdB1ATA5erwYmu0N9AAUR
tClSYWxmIFMuIEVuZ2Vsc2NoYWxsIDxyc2VAZW5nZWxzY2hhbGwuY29tPokAlQMF
EDNko/QOXq8GJrtDfQEBKVoD/2K/+4pcwhxok+FkuLwC5Pnuh/1oeOYHiKYwx0Z3
p09RLvDtNldr6VD+aL9JltxdPTARzZ8M50UqoF9jMr25GifheFYhilww41OVZA3e
cLXlLgda1+t0vWs3Eg/i2b0arQQDaIq7PeRdjdEDgwnG4xBaqaAqfgxwOXJ+LPWF
hiXZ

沒有最後=K7lL一部分,它指的是以 Base64 編碼的 CRC 校驗和。但如果我輸入:

$ echo -n "mQCNAzNko/QAAAEEANZ2kpN/oMkz4tqzxvKPZws/XwsD0Y+E5/y7P2DIw4uHS/4N
>     syQbgkdrZhPBlXDv68DQioHXWsb904qyr7iZB1LC5ItK9MgqlK+Z2mvPqsGbHM8J
>     +oYib8kf2zJ6HvrYrP7NYB0tN9YYum2ICtx+hIi6aKGXdB1ATA5erwYmu0N9AAUR
>     tClSYWxmIFMuIEVuZ2Vsc2NoYWxsIDxyc2VAZW5nZWxzY2hhbGwuY29tPokAlQMF
>     EDNko/QOXq8GJrtDfQEBKVoD/2K/+4pcwhxok+FkuLwC5Pnuh/1oeOYHiKYwx0Z3
>     p09RLvDtNldr6VD+aL9JltxdPTARzZ8M50UqoF9jMr25GifheFYhilww41OVZA3e
>     cLXlLgda1+t0vWs3Eg/i2b0arQQDaIq7PeRdjdEDgwnG4xBaqaAqfgxwOXJ+LPWF
>     hiXZ" | base64 -D | md5

我得到以下輸出:

4697e84969da935454c7f2cdc19aaf08

正如你所看到的,這不匹配00 C9 21 8E...

檢查 RFC 4880 ->https://www.rfc-editor.org/rfc/rfc4880#section-12.2:

對於 V3 金鑰,八個八位元組的金鑰 ID 由
RSA 金鑰的公共模數的低 64 位元組成。


V3 金鑰的指紋是透過使用 MD5 對形成金鑰材料(公共模數 n,後跟指數 e)的 MPI 主體(但不是兩個八位元組長度)進行雜湊處理而形成的。請注意,V3 密鑰
和 MD5 均已棄用。

V4 指紋是八位元組 0x99 的 160 位元 SHA-1 雜湊值,
後面跟著兩個八位元組資料包長度,最後是
從版本欄位開始的整個公鑰資料包。密鑰 ID 是指紋的低 64 位元。

它如何轉換為命令列命令?

編輯1:我正在嘗試這樣做pgpdump -i

$ pgpdump -i publickey.pub
Old: Public Key Packet(tag 6)(141 bytes)
    Ver 3 - old
    Public key creation time - Mon Apr 28 17:19:48 MSD 1997
    Valid days - 0[0 is forever]
    Pub alg - RSA Encrypt or Sign(pub 1)
    RSA n(1024 bits) - d6 76 92 93 7f a0 c9 33 e2 da b3 c6 f2 8f 67 0b 3f 5f 0b 03 d1 8f 84 e7 fc bb 3f 60 c8 c3 8b 87 4b fe 0d b3 24 1b 82 47 6b 66 13 c1 95 70 ef eb c0 d0 8a 81 d7 5a c6 fd d3 8a b2 af b8 99 07 52 c2 e4 8b 4a f4 c8 2a 94 af 99 da 6b cf aa c1 9b 1c cf 09 fa 86 22 6f c9 1f db 32 7a 1e fa d8 ac fe cd 60 1d 2d 37 d6 18 ba 6d 88 0a dc 7e 84 88 ba 68 a1 97 74 1d 40 4c 0e 5e af 06 26 bb 43 7d 
    RSA e(5 bits) - 11 
Old: User ID Packet(tag 13)(41 bytes)
    User ID - Ralf S. Engelschall <[email protected]>
Old: Signature Packet(tag 2)(149 bytes)
    Ver 3 - old
    Hash material(5 bytes):
        Sig type - Generic certification of a User ID and Public Key packet(0x10).
        Creation time - Mon Apr 28 17:19:48 MSD 1997
    Key ID - 0x0E5EAF0626BB437D
    Pub alg - RSA Encrypt or Sign(pub 1)
    Hash alg - MD5(hash 1)
    Hash left 2 bytes - 29 5a 
    RSA m^d mod n(1023 bits) - 62 bf fb 8a 5c c2 1c 68 93 e1 64 b8 bc 02 e4 f9 ee 87 fd 68 78 e6 07 88 a6 30 c7 46 77 a7 4f 51 2e f0 ed 36 57 6b e9 50 fe 68 bf 49 96 dc 5d 3d 30 11 cd 9f 0c e7 45 2a a0 5f 63 32 bd b9 1a 27 e1 78 56 21 8a 5c 30 e3 53 95 64 0d de 70 b5 e5 2e 07 5a d7 eb 74 bd 6b 37 12 0f e2 d9 bd 1a ad 04 03 68 8a bb 3d e4 5d 8d d1 03 83 09 c6 e3 10 5a a9 a0 2a 7e 0c 70 39 72 7e 2c f5 85 86 25 d9 
        -> PKCS-1

我應該如何提取模數和指數?我想我應該對這部分輸出做些事情:

Hash left 2 bytes - 29 5a 
RSA m^d mod n(1023 bits) - 62 bf fb 8a 5c c2 1c 68 93 e1 64 b8 bc 02 e4 f9 ee 87 fd 68 78 e6 07 88 a6 30 c7 46 77 a7 4f 51 2e f0 ed 36 57 6b e9 50 fe 68 bf 49 96 dc 5d 3d 30 11 cd 9f 0c e7 45 2a a0 5f 63 32 bd b9 1a 27 e1 78 56 21 8a 5c 30 e3 53 95 64 0d de 70 b5 e5 2e 07 5a d7 eb 74 bd 6b 37 12 0f e2 d9 bd 1a ad 04 03 68 8a bb 3d e4 5d 8d d1 03 83 09 c6 e3 10 5a a9 a0 2a 7e 0c 70 39 72 7e 2c f5 85 86 25 d9  

我試圖回顯這些十六進制數字的二進制值:

29 5a(散列左 2 個位元組)連接: 62 bf fb 8a 5c c2 1c 68 93 e1 64 b8 bc 02 e4 f9 ee 87 fd 68 78 e6 07 88 a6 30 c7 46 77 a7 4f 51 2e f0 ed 36 57 6b e9 50 fe 68 bf 49 96 dc 5d 3d 30 11 cd 9f 0c e7 45 2a a0 5f 63 32 bd b9 1a 27 e1 78 56 21 8a 5c 30 e3 53 95 64 0d de 70 b5 e5 2e 07 5a d7 eb 74 bd 6b 37 12 0f e2 d9 bd 1a ad 04 03 68 8a bb 3d e4 5d 8d d1 03 83 09 c6 e3 10 5a a9 a0 2a 7e 0c 70 39 72 7e 2c f5 85 86 25 d9

我最終得到的命令是:

$ echo -ne "\x29\x5a\x62\xbf\xfb\x8a\x5c\xc2\x1c\x68\x93\xe1\x64\xb8\xbc\x02\xe4\xf9\xee\x87\xfd\x68\x78\xe6\x07\x88\xa6\x30\xc7\x46\x77\xa7\x4f\x51\x2e\xf0\xed\x36\x57\x6b\xe9\x50\xfe\x68\xbf\x49\x96\xdc\x5d\x3d\x30\x11\xcd\x9f\x0c\xe7\x45\x2a\xa0\x5f\x63\x32\xbd\xb9\x1a\x27\xe1\x78\x56\x21\x8a\x5c\x30\xe3\x53\x95\x64\x0d\xde\x70\xb5\xe5\x2e\x07\x5a\xd7\xeb\x74\xbd\x6b\x37\x12\x0f\xe2\xd9\xbd\x1a\xad\x04\x03\x68\x8a\xbb\x3d\xe4\x5d\x8d\xd1\x03\x83\x09\xc6\xe3\x10\x5a\xa9\xa0\x2a\x7e\x0c\x70\x39\x72\x7e\x2c\xf5\x85\x86\x25\xd9" | md5

它應該輸出這些十六進制數字的二進制數據,然後計算該二進制數據的 MD5 哈希值,但我得到的哈希值仍然不同:

6f09f2ac5c5af1c6dd3833e584387103

我知道我做錯了,但我沒有找到有關如何pgpdump正確解釋輸出以及我應該連接然後散列的哪些部分的資訊...

編輯:感謝 Jens Erat,透過這個小小的“OpenPGP 指紋極客”,我可以得出這樣的結論:

為了V3 金鑰 RSA 1024 位散列鍵MD5,計算指紋129位元組組成128的位元組數RSA與MPI(從位元組偏移量開始14(前提是第一個位元組位於偏移量1) 匯出的原始 OpenPGP 公鑰與gpg --export $UID) 連接1byte 是偏移量處的位元組144因此省略了2偏移處的長度位元組142143,正如 RFC 4880 所說。

以下命令使用原始 GPG 資料計算指紋:

gpg --export $UID | xxd -p | tr -d '\n ' | tail \
-c +27 | cut -c -256,261-262 | sed -e 's/[0-9a-fA-F]\{2\}/\\\\x&/g' | while read TMP; do \
echo -ne $TMP; done | md5 | sed -e 's/[0-9a-f]\{2\}/ &/g' | \
awk '{print "\n MD5 fingerprint:"toupper($0)"\n"}' 

其中 $UID 是金鑰持有者的 UID。

對於 OpenPGP V4 RSA 公鑰,情況有所不同:

對於 2048 位元 RSA 公鑰,指紋是透過使用 SHA1 對原始 OpenPGP 金鑰資料的前 272 位元組進行雜湊處理來獲得的:

gpg --export $UID | head -c 272 | shasum | grep -Eo "[0-9a-f]+" | sed -e 's/[0-9a-f]\{4\}/ &/g' | \
awk '{print "\n RSA 2048 bit SHA1 fingerprint:"toupper($0)"\n"}'

對於 4096 位元 RSA 公鑰,指紋是透過使用 SHA1 對原始 OpenPGP 金鑰資料的前 528 位元組進行雜湊處理來獲得的:

gpg --export $UID | head -c 528 | shasum | \
grep -Eo "[0-9a-f]+" | sed -e 's/[0-9a-f]\{4\}/ &/g' | \
awk '{print "\n RSA 4096 SHA1 fingerprint:"toupper($0)"\n"}'

應該夠了。無論如何,使用gpgsplitV4鍵似乎更便攜。

答案1

對於 OpenPGP 金鑰,它並不像 SSH 那麼容易。指紋不是根據整個 Base64 編碼的公鑰計算的,而是僅根據其某些(二進位)部分計算的。

對於版本 3 OpenPGP 金鑰,您需要做的是:

  1. 解析OpenPGP公鑰資料包
  2. 對於 RSA,提取模數和指數
  3. 連接它們的二進制值
  4. 計算哈希值

對於步驟 1 和 2,您可以依賴該工具pgpdump,它可以使用標誌解析並輸出數字-i

對於版本 4 金鑰,情況甚至變得更加複雜。

如果您想出於教育目的計算哈希和,我建議您進行擴展pgpdump並使用所有可用的解析器程式碼,以便您可以直接處理提取的資訊。我非常確定處理二進位資訊也比使用純 shell 程式碼更容易。

更新:您使用了錯誤的整數。使用行中的RSA nRSA e來代替。使用標準工具將所有內容放在幾行中:

pgpdump -i publickey.pub | \
grep -E '(RSA n|RSA e)' | \
cut -d'-' -f2 | \
tr -d "\n " | \
perl -e 'print pack "H*", <STDIN>' | \
md5sum

pgpdump -i轉儲密鑰的 MPI,我們將其從不需要的所有內容中grep取出,翻譯掉所有空白,使用轉換為二進制,最後計算.cuttrperlmd5sum

在您提供的密鑰上運行:

$ pgpdump -i publickey.pub | \
> grep -E '(RSA n|RSA e)' | \
> cut -d'-' -f2 | \
> tr -d "\n " | \
> perl -e 'print pack "H*", <STDIN>' | \
> md5sum
00c9218ed1ab7037dd67a23a0a6f8da5  -

似乎正是我們正在尋找的東西。

為了完整起見,版本 4 金鑰也是如此,您需要另一個工具鏈。我們需要完整的公鑰資料包。要分解OpenPGP消息,gpgsplit就派上用場了。之後,您可以立即計算sha1sum文件的:

gpgsplit publickey.pub; sha1sum *.public_key

例如,使用我自己的密鑰運行:

$ gpgsplit publickey.pub; sha1sum *.public_key
0d69e11f12bdba077b3726ab4e1f799aa4ff2279  000001-006.public_key

相關內容