Generación de claves RSA en formato PKCS # 1 en Java

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 ambos como PKCS # 1. es posible? Pasé una cantidad considerable de tiempo revisando los documentos de Java pero no encontré una solución. El resultado es el mismo cuando uso Java y los proveedores de 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

Necesitarás 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 fragmentos de código que se muestran a continuación se han comprobado y se han encontrado trabajando con Bouncy Castle 1.52.

Llave privada

Convertir 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 comando OpenSSL que el formato de la clave es el esperado:

 openssl rsa -in rsa_private_key.pem -noout -text 

Llave pública

Convertir la 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 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 comando OpenSSL que el formato de la clave es el esperado:

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

Gracias

Muchas gracias a los autores de las siguientes publicaciones:

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.

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

 PrivateKeyInfo :: = SECUENCIA {
   versión de la versión,
   privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
   privateKey PrivateKey,
   atributos [0] Atributos IMPLÍCITOS OPCIONALES}

donde privateKey es:

“… una cadena de octetos cuyos contenidos son 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 encoding BER de un valor de tipo RSAPrivateKey “.

Esta estructura RSAPrivateKey es solo la encoding PKCS # 1 de la clave, que podemos extraer usando 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. 

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

 /***************************************** convert pkcs8 private key file to pkcs1 2013-1-25 Larry Wu created ****************************************/ #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  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  \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; } 

El marco de BouncyCastle tiene un codificador PKCS1 para resolver esto: http://www.bouncycastle.org/docs/docs1.6/index.html

Intenté generar claves públicas RSA compatibles con OpenSSL en formato DER utilizando 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 todavía era 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 ten una bonita 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 usar para encriptar:

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

Sé que esta es una publicación anterior. pero pasé dos días para resolver este problema y finalmente encontré que BouncyCastle puede hacer eso

ASN1Encodable

http://www.bouncycastle.org/docs/docs1.5on/org/bouncycastle/asn1/ASN1Encodable.html