4

У меня есть следующий открытый ключ GPG, хранящийся в файле publickey.pub и закодированный в 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 , которая относится к контрольной сумме CRC, закодированной в Base64. Но если я наберу:

$ 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://tools.ietf.org/html/rfc4880#section-12.2:

Для ключа V3 идентификатор ключа из восьми октетов состоит из младших 64 битов
открытый модуль ключа RSA.

Отпечаток ключа V3 формируется путем хэширования тела (но не двух-октетной длины) MPI, которые формируют материал ключа (общедоступный).
модуль n, за которым следует показатель е) с MD5. Обратите внимание, что обе клавиши V3
и MD5 устарели.

Отпечаток V4 - это 160-битный хэш SHA-1 октета 0x99,
сопровождаемый длиной в два октета пакета, сопровождаемый всей
Пакет с открытым ключом, начиная с поля версии. Идентификатор ключа представляет собой младшие 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 <rse@engelschall.com>
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 1024-битные ключи RSA, хэшированные с помощью MD5, отпечаток пальца вычисляется по 129 байтам, составленным из 128 байтов RSA n MPI (который начинается со смещения байтов 14 (при условии, что первый байт находится со смещением 1) от необработанный открытый ключ OpenPGP, экспортированный с помощью gpg --export $UID), соединенный с 1 байтом, который является байтом со смещением 144 и поэтому пропускает 2 байта длины со смещением 142 и 143, как говорится в 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 отпечаток пальца получается путем хеширования первых 272 байтов необработанных данных ключа OpenPGP с помощью SHA1:

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 отпечаток пальца получается путем хеширования первых 528 байтов необработанных данных ключа OpenPGP с помощью SHA1:

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"}'

Должно быть достаточно. В любом случае, использование gpgsplit с ключами V4 кажется более переносимым.

1 ответ1

4

Для ключей OpenPGP это не так просто, как с SSH. Отпечаток пальца вычисляется не из всего открытого ключа в кодировке Base64, а только по некоторым (двоичным) его частям.

Для ключа версии 3 OpenPGP вам нужно сделать следующее:

  1. Разобрать пакет открытого ключа OpenPGP
  2. Для RSA, модуль извлечения и экспонента
  3. Объединить их двоичные значения
  4. Рассчитать хэш-сумму

Для шагов 1 и 2 вы можете использовать инструмент pgpdump , который может анализировать и выводить числа, используя флаг -i .

Для ключей версии 4 это становится еще сложнее.

Если вы хотите вычислить хэш-сумму для образовательных целей, я бы рекомендовал вместо этого расширить pgpdump и использовать весь доступный код синтаксического анализатора, чтобы вы могли напрямую работать с извлеченной информацией. Я уверен, что обрабатывать двоичную информацию будет проще, чем с использованием чистого шелл-кода.

ОБНОВЛЕНИЕ: Вы использовали неправильные целые числа. Вместо этого используйте строки в строках RSA n и RSA 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 отвалов МУП Ключом, который мы grep из, cut eeverything нам не нужно, tr anslate прочь все пробелы, преобразовать в двоичную с помощью perl и , наконец , вычислить md5sum

Бег на ключе при условии:

$ 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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .