Tengo la siguiente clave pública GPG almacenada dentro de un archivo llamado publickey.pub
y codificado en 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-----
Si escribo:
$ gpg --with-fingerprint publickey.pub
Obtengo la huella digital de la clave:
Huella digital clave = 00 C9 21 8E D1 AB 70 37 DD 67 A2 3A 0A 6F 8D A5
Ahora bien, ¿cómo lo hace GPG? Quiero decir, ¿hay algún comando que pueda ejecutar sin usarlo gpg
y seguir obteniendo la misma huella digital? Con SSH, por ejemplo, dada una clave pública puedo hacer lo siguiente:
$ cat ~/.ssh/id_rsa.pub | awk '{print $2}' | base64 -D | md5
Y eso devolverá el mismo hash que:
$ ssh-keygen -l -f ~/.ssh/id_rsa.pub
Sé que el contenido real de la clave pública debería ser:
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
Sin la última =K7lL
parte, que hace referencia a la suma de comprobación CRC codificada en Base64. Pero si escribo:
$ 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
Obtengo el siguiente resultado:
4697e84969da935454c7f2cdc19aaf08
Lo cual, como puedes ver, no coincide.00 C9 21 8E...
Comprobando el RFC 4880 ->https://www.rfc-editor.org/rfc/rfc4880#section-12.2:
Para una clave V3, la ID de clave de ocho octetos consta de los 64 bits bajos del
módulo público de la clave RSA.La huella digital de una clave V3 se forma aplicando hash al cuerpo (pero no a la longitud de dos octetos) de los MPI que forman el material de la clave (
módulo público n, seguido del exponente e) con MD5. Tenga en cuenta que tanto las claves V3
como MD5 están en desuso.Una huella digital V4 es el hash SHA-1 de 160 bits del octeto 0x99,
seguido de la longitud del paquete de dos octetos, seguido del
paquete de clave pública completo comenzando con el campo de versión. La ID de clave son los 64 bits de orden inferior de la huella digital.
¿Cómo se traduce en un comando de línea de comando?
EDITAR 1: estoy intentando hacer eso con 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
¿Cómo debo extraer el módulo y el exponente? Supongo que debería hacer algo con esta parte del resultado:
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
Intenté hacer eco de los valores binarios de esos dígitos hexadecimales:
29 5a
(Hash dejó 2 bytes) concatenado con:
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
Y el comando con el que terminé fue:
$ 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
Lo que debería generar los datos binarios de esos dígitos hexadecimales y luego calcular el hash MD5 en esos datos binarios, pero el hash que obtengo sigue siendo diferente:
6f09f2ac5c5af1c6dd3833e584387103
Sé que lo estoy haciendo mal, pero no encontré información sobre cómo interpretar el pgpdump
resultado correctamente y qué partes de lo que debo concatenar y luego aplicar hash...
EDITAR:Gracias a Jens Erat, con este pequeño "geek de las huellas dactilares de OpenPGP" puedo concluir que:
ParaClaves V3 RSA 1024 bitsclaves hash conMD5, la huella digital se computa contra129bytes formados por128bytes de laRSA y MPI(que comienza en el desplazamiento de bytes14(siempre que el primer byte esté en desplazamiento1) de la clave pública OpenPGP sin procesar exportada con gpg --export $UID
) concatenada con1byte que es el byte en el desplazamiento144y por lo tanto omitiendo el2bytes de longitud en el desplazamiento142y143, como dice RFC 4880.
El siguiente comando calcula la huella digital utilizando datos GPG sin procesar y:
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"}'
Donde $UID es el UID del titular de la clave.
Para las claves públicas OpenPGP V4 RSA, la historia es diferente:
Para claves públicas RSA de 2048 bits, la huella digital se obtiene aplicando hash a los primeros 272 bytes de los datos de la clave OpenPGP sin procesar con 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"}'
Para claves públicas RSA de 4096 bits, la huella digital se obtiene aplicando hash a los primeros 528 bytes de los datos de la clave OpenPGP sin procesar con 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"}'
Debería ser suficiente. De todos modos, usarlo gpgsplit
con teclas V4 parece ser más portátil.
Respuesta1
Para las claves OpenPGP, no es tan fácil como con SSH. La huella digital no se calcula a partir de toda la clave pública codificada en Base64, sino solo de algunas partes (binarias) de la misma.
Para una clave OpenPGP versión 3, lo que tendrías que hacer es:
- Analizar el paquete de clave pública OpenPGP
- Para RSA, extraiga módulo y exponente
- Concatenar sus valores binarios
- Calcular el hashsum
Para los pasos 1 y 2, puede confiar en la herramienta pgpdump
, que puede analizar y generar los números usando la -i
bandera.
Para las claves de la versión 4, la cosa se vuelve aún más complicada.
Si desea calcular el hashsum con fines educativos, le recomiendo ampliarlo pgpdump
y utilizar todo el código del analizador disponible, para que pueda trabajar directamente en la información extraída. Estoy bastante seguro de que manejar la información binaria será más fácil que con el código shell puro.
ACTUALIZAR:Usaste los números enteros incorrectos. Utilice los que están en líneas RSA n
y RSA e
en su lugar. Reuniendo todo en unas pocas líneas usando herramientas estándar:
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
Vuelca los MPI de la clave, que grep
eliminamos cut
de todo lo que no necesitamos, tr
anulamos todos los espacios en blanco, los convertimos a binario perl
y finalmente calculamos el archivo md5sum
.
Ejecutando con la clave que proporcionó:
$ pgpdump -i publickey.pub | \
> grep -E '(RSA n|RSA e)' | \
> cut -d'-' -f2 | \
> tr -d "\n " | \
> perl -e 'print pack "H*", <STDIN>' | \
> md5sum
00c9218ed1ab7037dd67a23a0a6f8da5 -
Parece ser más o menos lo que estamos buscando.
Para completar, lo mismo para las claves de la versión 4, donde necesita otra cadena de herramientas. Necesitamos el paquete de clave pública completo. Para descomponer un mensaje OpenPGP, gpgsplit
resulta útil. Luego, puedes calcular inmediatamente el tamaño sha1sum
del archivo:
gpgsplit publickey.pub; sha1sum *.public_key
Por ejemplo, ejecutando con mi propia clave:
$ gpgsplit publickey.pub; sha1sum *.public_key
0d69e11f12bdba077b3726ab4e1f799aa4ff2279 000001-006.public_key