java rsa x509 pkcs#1

Generando claves RSA en formato PKCS#1 en Java



x509 (6)

Desde RFC5208 , el formato no cifrado PKCS # 8 consiste en una estructura PrivateKeyInfo :

PrivateKeyInfo ::= SEQUENCE { version Version, privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, privateKey PrivateKey, attributes [0] IMPLICIT Attributes OPTIONAL }

donde privateKey es:

"... una cadena de octetos cuyo contenido es el valor de la clave privada. La interpretación de los contenidos se define en el registro del algoritmo de clave privada. Para una clave privada RSA, por ejemplo, los contenidos son una codificación BER de un valor de tipo RSAPrivateKey ".

Esta estructura RSAPrivateKey es solo la codificación PKCS # 1 de la clave, que podemos extraer utilizando BouncyCastle:

// pkcs8Bytes contains PKCS#8 DER-encoded key as a byte[] PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes); RSAPrivateKeyStructure pkcs1Key = RSAPrivateKeyStructure.getInstance( pki.getPrivateKey()); byte[] pkcs1Bytes = pkcs1Key.getEncoded(); // etc.

Cuando genero un par de claves RSA utilizando la API de Java, la clave pública está codificada en el formato X.509 y la clave privada está codificada en el formato PKCS # 8. Estoy buscando codificar tanto como PKCS # 1. es posible? He pasado una cantidad considerable de tiempo revisando los documentos de Java pero no he encontrado una solución. El resultado es el mismo cuando uso los proveedores de Java y Bouncy Castle.

Aquí hay un fragmento del código:

KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA","BC"); keygen.initialize(1024); KeyPair pair = keygen.generateKeyPair(); PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); byte[] privBytes = priv.getEncoded(); byte[] pubBytes = pub.getEncoded();

Las dos matrices de bytes resultantes tienen el formato X.509 (público) y PKCS # 8 (privado).

Cualquier ayuda sería muy apreciada. Hay algunos mensajes similares pero ninguno realmente responde mi pregunta.

Gracias



Escribí un programa en C para convertir la clave privada pkcs8 a pkcs1. ¡Funciona!

/***************************************** convert pkcs8 private key file to pkcs1 2013-1-25 Larry Wu created ****************************************/ #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <openssl/rsa.h> #include <openssl/bio.h> #include <openssl/err.h> #include <openssl/pem.h> #include <openssl/engine.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <assert.h> #include <stdarg.h> #include <fstream> #include <sstream> #include <iostream> #include <map> #include <set> #include <list> #include <vector> using namespace std; #define MY_TRACE_ERROR printf /* gcc -Wall -o pkcs_8to1 pkcs_8to1.cpp -g -lstdc++ -lcrypto -lssl */ int main(int argc, char **argv) { EVP_PKEY * pkey = NULL; string kin_fname; FILE *kin_file = NULL; string kout_fname; FILE *kout_file = NULL; // param if(argc != 3) { printf("Usage: %s <pkcs8_key_file> <pkcs1_key_file>/n", argv[0]); return 1; } kin_fname = argv[1]; kout_fname = argv[2]; // init OpenSSL_add_all_digests(); ERR_load_crypto_strings(); // read key if((kin_file = fopen(kin_fname.c_str(), "r")) == NULL) { MY_TRACE_ERROR("kin_fname open fail:%s/n", kin_fname.c_str()); return 1; } if ((pkey = PEM_read_PrivateKey(kin_file, NULL, NULL, NULL)) == NULL) { ERR_print_errors_fp(stderr); MY_TRACE_ERROR("PEM_read_PrivateKey fail/n"); fclose(kin_file); return 2; } // write key if((kout_file = fopen(kout_fname.c_str(), "w")) == NULL) { MY_TRACE_ERROR("kout_fname open fail:%s/n", kout_fname.c_str()); return 1; } if (!PEM_write_PrivateKey(kout_file, pkey, NULL, NULL, 0, NULL, NULL)) { ERR_print_errors_fp(stderr); MY_TRACE_ERROR("PEM_read_PrivateKey fail/n"); fclose(kout_file); return 2; } // clean fclose(kin_file); fclose(kout_file); EVP_PKEY_free(pkey); return 0; }


Estaba intentando generar claves públicas RSA compatibles con OpenSSL en formato DER usando la biblioteca BountyCastle J2ME portada a BlackBerry, mi código:

public void testMe() throws Exception { RSAKeyPairGenerator generator = new RSAKeyPairGenerator(); generator.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), 512, 80)); AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); RSAKeyParameters params = (RSAKeyParameters) keyPair.getPublic(); RSAPublicKeyStructure struct = new RSAPublicKeyStructure(params.getModulus(), params.getExponent()); SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(new AlgorithmIdentifier("1.2.840.113549.1.1.1"), struct); byte[] bytes = info.getDEREncoded(); FileOutputStream out = new FileOutputStream("/tmp/test.der"); out.write(bytes); out.flush(); out.close(); }

La clave seguía siendo incorrecta:

$ openssl asn1parse -in test.der -inform DER -i 0:d=0 hl=2 l= 90 cons: SEQUENCE 2:d=1 hl=2 l= 11 cons: SEQUENCE 4:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 15:d=1 hl=2 l= 75 prim: BIT STRING

Cambié org.bouncycastle.asn1.x509.AlgorithmIdentifier

public AlgorithmIdentifier( String objectId) { this.objectId = new DERObjectIdentifier(objectId); // This line has been added this.parametersDefined = true; }

Y ahora tienen buena llave:

$ openssl asn1parse -in test.der -inform DER -i 0:d=0 hl=2 l= 92 cons: SEQUENCE 2:d=1 hl=2 l= 13 cons: SEQUENCE 4:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 15:d=2 hl=2 l= 0 prim: NULL 17:d=1 hl=2 l= 75 prim: BIT STRING

Que se puede utilizar para cifrar:

$ echo "123" | openssl rsautl -pubin -inkey test.der -encrypt -keyform DER -out y $ wc -c y 64 y


Necesitará BouncyCastle:

import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemWriter;

Los siguientes fragmentos de código se han verificado y se han encontrado trabajando con Bouncy Castle 1.52.

Llave privada

Convertir la clave privada de PKCS8 a PKCS1:

PrivateKey priv = pair.getPrivate(); byte[] privBytes = priv.getEncoded(); PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes); ASN1Encodable encodable = pkInfo.parsePrivateKey(); ASN1Primitive primitive = encodable.toASN1Primitive(); byte[] privateKeyPKCS1 = primitive.getEncoded();

Convertir clave privada en PKCS1 a PEM:

PemObject pemObject = new PemObject("RSA PRIVATE KEY", privateKeyPKCS1); StringWriter stringWriter = new StringWriter(); PemWriter pemWriter = new PemWriter(stringWriter); pemWriter.writeObject(pemObject); pemWriter.close(); String pemString = stringWriter.toString();

Verifique con la línea de comandos OpenSSL que el formato de la clave sea el esperado:

openssl rsa -in rsa_private_key.pem -noout -text

Llave pública

Convierta una clave pública de X.509 SubjectPublicKeyInfo a PKCS1:

PublicKey pub = pair.getPublic(); byte[] pubBytes = pub.getEncoded(); SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(pubBytes); ASN1Primitive primitive = spkInfo.parsePublicKey(); byte[] publicKeyPKCS1 = primitive.getEncoded();

Convertir la clave pública en PKCS1 a PEM:

PemObject pemObject = new PemObject("RSA PUBLIC KEY", publicKeyPKCS1); StringWriter stringWriter = new StringWriter(); PemWriter pemWriter = new PemWriter(stringWriter); pemWriter.writeObject(pemObject); pemWriter.close(); String pemString = stringWriter.toString();

Verifique con la línea de comandos OpenSSL que el formato de la clave sea el esperado:

openssl rsa -in rsa_public_key.pem -RSAPublicKey_in -noout -text

Gracias

Muchas gracias a los autores de los siguientes posts:

  • https://.com/a/8713518/1016580
  • https://.com/a/14052651/1016580
  • https://.com/a/14068057/1016580

Esas publicaciones contenían información útil, aunque a veces obsoleta (es decir, para versiones anteriores de BouncyCastle), que me ayudó a construir esta publicación.