ECDSA: ¿Por qué las claves públicas generadas por ssh-keygen y Java tienen diferentes tamaños?

4

Estoy usando ssh-keygen -t ecdsa -b 256 lo que genera esto para la parte pública:

ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNaHgNjXShzF/hmZOuhTCCsokgpSCyFohETHT4+OQMxW5g+d9nZCYxpDhwivuWbsoXTYpQWlLATXxjbQr2Y3KRY= t0132456@L541918

Necesito hacer lo mismo en Java. Estoy usando BouncyCastle:

ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
g.initialize(ecSpec, new SecureRandom());
KeyPair pair = g.generateKeyPair();

ECPublicKey publicKey = (ECPublicKey)pair.getPublic();
System.out.println("kpub=" + Base64.toBase64String(publicKey.getEncoded()));

pero la clave pública es más pequeña y no comienza con el mismo encabezado:

kpub=MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvHW0nR1hAzy3BuQR8mMkHqgGvvdXLZWAXS29fkFbhshr8y6xybHh0LRDoaUciYgr3w7WGKpfxFSSFqSjdG8bww==

¿Cómo generarse de la misma manera que ssh-genkey pero en Java?

    
pregunta akira2x3x 13.07.2016 - 02:18
fuente

1 respuesta

10

Los tamaños son diferentes porque los formatos son diferentes. Las cadenas que muestra son valores binarios codificados en Base64 . En todo este mensaje, hablo sobre los valores binarios, es decir, supongo que primero descodificaste las cadenas Base64 en binario.

Matemáticamente, una clave pública ECDSA es un punto en una curva elíptica. Un punto tiene dos coordenadas, llamadas X y Y , que están unidas entre sí a través de la ecuación de curva (en ese caso, Y 2 = X 3 + aX + b para dos constantes a y b que definen la curva específica). Aquí, utiliza la curva NIST P-256, que se ha especificado para trabajar en un campo de 256 bits, es decir, X y Y son dos enteros de 256 bits. El ANSI X9.62 standard define un formato de 65 bytes para representar tal < em> X y Y ; básicamente, un primer byte de valor 0x04, seguido de X sobre exactamente 32 bytes (en notación big-endian), luego Y sobre otros 32 bytes.

Hasta ahora todo bien. Luego, tanto Java como SSH envuelven ese valor de 65 bytes en una estructura que también identifica explícitamente la curva a través de algunos identificadores simbólicos. Lo hacen de manera diferente.

Java sigue la codificación basada en ASN.1 también especificada en ANSI X9.62. Hay varias opciones, pero, en su caso, tiene un SEQUENCE cuyo contenido es un sub- anidado SEQUENCE , luego un BIT STRING . El sub anidado SEQUENCE contiene dos valores de OBJECT IDENTIFIER , el primero es 1.2.840.10045.2.1 (que significa "esta es una clave pública de curva elíptica"), y el segundo es 1.2.840.10045.3.1.7, que Designa la curva NIST P-256. Los contenidos BIT STRING son, exactamente, la codificación de 65 bytes del punto de curva. Para resumir: el valor de Java, cuando se descodifica Base64, produce un valor de 91 bytes de longitud, los últimos 65 bytes de los cuales son la codificación de X y Y .

Por otro lado, OpenSSH es bien conocido por una alergia crónica a los estándares y, por supuesto, inventaron su propio formato. Luego se documentó en RFC 5656, sección 3.1 . Básicamente, la clave codificada consiste en tres "cadenas", una después de la otra. Cada cadena consta de un encabezado de 32 bits (4 bytes, notación big-endian) seguido del valor de la cadena; El encabezado contiene la longitud del valor, en bytes. La primera cadena es la codificación ASCII de "ecdsa-sha2-nistp256" (esto es identifica el algoritmo de firma). La segunda cadena es la codificación ASCII de "nistp256" (esto identifica la curva, redundantemente con la primera cadena). La tercera cadena tiene un valor de 65 bytes y, lo has adivinado, esa es la codificación de 65 bytes de X y Y . La longitud total es de 104 bytes, y la codificación de X y Y utiliza los últimos 65 de estos 104 bytes.

Básicamente, en ambos formatos, la parte interesante es exactamente los últimos 65 bytes; el resto es solo un identificador de la curva involucrada, en dos dialectos distintos. Si genera muchas claves con ssh-keygen , notará que diferirán solo en sus últimos 65 bytes (nuevamente, después de la decodificación Base64). De manera similar con la producción de muchas claves en Java.

Por lo tanto, la conversión de una clave pública producida en Java a una clave pública compatible con SSH es tan simple como tomar los últimos 65 bytes de esa clave (después de la decodificación de Base64, cuando corresponda), y concatenarlos después del encabezado de 39 bytes. de una clave SSH.

    
respondido por el Thomas Pornin 13.07.2016 - 03:09
fuente

Lea otras preguntas en las etiquetas