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
El marco BouncyCastle tiene un codificador PKCS1 para resolver esto: http://www.bouncycastle.org/docs/docs1.6/index.html
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.
Sé que este es el viejo post. pero pasé dos días para resolver este problema y, finalmente, encontrar que BouncyCastle puede hacer eso
ASN1Encodable
http://www.bouncycastle.org/docs/docs1.5on/org/bouncycastle/asn1/ASN1Encodable.html