¿Por qué son similares los primeros caracteres de una clave privada DSA?

1

Noté que la mayoría de las claves privadas de DSA a menudo comienzan con los mismos pocos caracteres, MIIBvAIBAAKBgQD .

Por ejemplo, genere una clave privada en Ubuntu ejecutando:

ssh-keygen -t dsa -N '' -f /tmp/id_dsa

Esto da como resultado un archivo de clave privada que comienza con algo como:

-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQD...

Los primeros 16 caracteres son sospechosamente similares.
Espero que ssh-keygen esté utilizando un nonce aleatorio antes de comenzar el cifrado. Suponiendo que ssh-keygen está utilizando una fuente aleatoria, ¿por qué los primeros caracteres de los archivos de clave privada DSA son similares?

Al usar un script, descubrí que la singularidad ("aleatoriedad", buena entropía, etc.) comienza en el carácter 17 °.

leading char count  1 - unique combinations   1 among 100 generated keys
leading char count  2 - unique combinations   1 among 100 generated keys
leading char count  3 - unique combinations   1 among 100 generated keys
leading char count  4 - unique combinations   1 among 100 generated keys
leading char count  5 - unique combinations   2 among 100 generated keys
leading char count  6 - unique combinations   4 among 100 generated keys
leading char count  7 - unique combinations   6 among 100 generated keys
leading char count  8 - unique combinations   5 among 100 generated keys
leading char count  9 - unique combinations   4 among 100 generated keys
leading char count 10 - unique combinations   4 among 100 generated keys
leading char count 11 - unique combinations   4 among 100 generated keys
leading char count 12 - unique combinations   4 among 100 generated keys
leading char count 13 - unique combinations   4 among 100 generated keys
leading char count 14 - unique combinations   4 among 100 generated keys
leading char count 15 - unique combinations   7 among 100 generated keys
leading char count 16 - unique combinations  87 among 100 generated keys
leading char count 17 - unique combinations 100 among 100 generated keys
leading char count 18 - unique combinations 100 among 100 generated keys
leading char count 19 - unique combinations 100 among 100 generated keys
leading char count 20 - unique combinations 100 among 100 generated keys
leading char count 21 - unique combinations 100 among 100 generated keys
...

Usé el siguiente código de bash para determinar esto

keyf=/tmp/id_dsa-${RANDOM}
for upto in {1..35} ; do
    keys="${keyf}-${upto}"
    rm -f "${keys}" &>/dev/null
    for i in {0..99} ; do
        rm "${keyf}" &>/dev/null
        ssh-keygen -t dsa -N '' -f "${keyf}" &>/dev/null
        sed '2q;d' "${keyf}" | cut -b 1-"${upto}" >> "${keys}"
    done
    keys_count_all=$(cat "${keys}" | wc -l)
    keys_count_uniq=$(sort -u "${keys}" | wc -l)
    printf "leading char count %2d - unique combinations %3d among %3d generated keys\n" ${upto} ${keys_count_uniq} ${keys_count_all}
done
rm "${keyf}" 
    
pregunta JamesThomasMoon1979 30.01.2014 - 08:04
fuente

2 respuestas

5

El archivo de clave privada DSA tiene una estructura , no es simplemente un número aleatorio (o dos). Dentro de los datos codificados PEM (base64 + delimitadores) hay una estructura binaria ASN.1 codificada en DER. ASN.1 es una forma independiente del sistema para codificar datos (considérelo como un "XML binario"). Estrictamente, solo los primeros dos bytes de una clave DSA codificada de esta manera son fijos, pero para un dado el tamaño de la clave se puede decir más. Tenga en cuenta que no hay "números mágicos" en la estructura de datos subyacente.

Un par de teclas DSA tiene 5 componentes distintos: P, Q, G, parte de clave pública, parte de clave privada. Cavando en la fuente de OpenSSL (utilizada por OpenSSH para esto) crypto/dsa/dsa_asn1.c :

ASN1_SEQUENCE_cb(DSAPrivateKey, dsa_cb) = {
    ASN1_SIMPLE(DSA, version, LONG),
    ASN1_SIMPLE(DSA, p, BIGNUM),
    ASN1_SIMPLE(DSA, q, BIGNUM),
    ASN1_SIMPLE(DSA, g, BIGNUM),
    ASN1_SIMPLE(DSA, pub_key, BIGNUM),
    ASN1_SIMPLE(DSA, priv_key, BIGNUM)
} ASN1_SEQUENCE_END_cb(DSA, DSAPrivateKey)

Lo que corresponde con lo que verá con openssl asn1parse -in id_dsa (aunque tenga en cuenta que si utiliza openssl dsa -in id_dsa -noout -text , el campo de la versión se omite y el orden se ha cambiado a priv, pub, P, Q , G).

Echando un vistazo a la salida ASN.1 analizada:

0:d=0  hl=4 l= 830 cons: SEQUENCE          
4:d=1  hl=2 l=   1 prim: INTEGER           :00
7:d=1  hl=4 l= 257 prim: INTEGER           :DFA34F1D[...]

En la forma DER de ASN.1 cada elemento de datos es un (type tag,length,data) triple. En la salida de asn1parse anterior, las columnas iniciales indican el desplazamiento, la profundidad de encabezado de d= , la longitud de encabezado de hl= (tamaño de la etiqueta de tipo y los campos de longitud) y la longitud de datos de l= . (En formas que no son de DER, puede ser un poco más complicado, pero no tenemos que preocuparnos por eso aquí).

Si desenvolvemos la base64 podemos ver el formato binario DER:

$ openssl dsa -in id_dsa -outform DER | hexdump -C | head -1

00000000  30 82 03 3e 02 01 00 02  82 01 01 00 df a3 4f 1d  |0..>........ߣO.|
                                            ^^ ^^ ^^ ^^ ^^ 

Puede esperar que los bytes iniciales sean razonablemente estáticos (SEQUENCE "container" + version = 0), aunque el tamaño de la secuencia dependerá del tamaño de la clave DSA. El DSA "P" ( ^^ ) es el primer largo (1024, 2048 o 3072 bits) es el siguiente, por lo que para un tamaño de clave fija determinado, su encabezado de 4 bytes también será razonablemente estático. Esto da un total de 11 bytes razonablemente estáticos para un tamaño de clave definido (técnicamente tamaños , por ejemplo, 2048,256 para p, q). Una complicación aquí son las pequeñas variaciones en el tamaño de los enteros codificados: se agregará un relleno adicional de 0 bytes para garantizar enteros positivos (el bit inicial debe ser 0).

Analizó los datos codificados en base64 , esos 11 bytes estáticamente razonables se codifican en los primeros 15 caracteres de la salida base64 ( 11 * 8/6 = 14.66) con dos bits del P aleatorio también encontrados en el carácter 15 (dada la condición de relleno, esos dos bits serán cero aproximadamente el 75% del tiempo de todos modos). Creo que esto está de acuerdo con sus datos.

Tenga en cuenta el pequeño aumento en la variación alrededor del carácter codificado en la base 5ta / 6ta: esto corresponde con el octeto bajo del tamaño de la estructura total (4to byte DER), el tamaño tiene variaciones menores para los tamaños de clave fija debido al 50% posibilidad de liderar el relleno 0 en cada uno de los 5 enteros DSA codificados.

Un formato alternativo para las claves es PKCS # 8 , esto omite la parte de clave pública, pero agrega la dsaEncryption OID por lo que al menos la estructura es un poco menos enigmática:

$ openssl pkcs8 -in id_dsa -topk8 -nocrypt | openssl asn1parse
    0:d=0  hl=4 l= 589 cons: SEQUENCE          
    4:d=1  hl=2 l=   1 prim: INTEGER           :00
    7:d=1  hl=4 l= 558 cons: SEQUENCE          
   11:d=2  hl=2 l=   7 prim: OBJECT            :dsaEncryption
   20:d=2  hl=4 l= 545 cons: SEQUENCE          
   24:d=3  hl=4 l= 257 prim: INTEGER           :DFA34F[...]
    
respondido por el mr.spuratic 30.01.2014 - 14:45
fuente
1

Su clave DSA se almacena como una estructura de datos codificada por DER que a su vez es base64 -codificado. Los primeros 16 bytes son básicamente la codificación de una descripción de lo que hay en el archivo; el resto del archivo es la clave en sí.

    
respondido por el Mark 30.01.2014 - 10:44
fuente

Lea otras preguntas en las etiquetas