Генерация ключей RSA в формате PKCS#1 в Java

Когда я генерирую пару ключей RSA с помощью API Java, открытый ключ кодируется в формате X.509, а закрытый ключ - в формате PKCS#8. Я ищу кодировать как PKCS#1. Это возможно? Я потратил значительное количество времени на просмотр документации по Java, но не нашел решения. Результат тот же, когда я использую провайдеров Java и Bouncy Castle.

Вот фрагмент кода:

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();

Два результирующих байтовых массива форматируются как X.509 (общедоступный) и PKCS#8 (частный).

Любая помощь приветствуется. Есть несколько похожих постов, но никто не отвечает на мой вопрос.

Благодарю вас

6 ответов

Вам понадобится 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;

Фрагменты кода ниже были проверены и найдены работающими с Bouncy Castle 1.52.

Закрытый ключ

Преобразовать закрытый ключ из PKCS8 в 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();

Преобразовать закрытый ключ в PKCS1 в 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();

Проверьте с помощью командной строки OpenSSL, что формат ключа соответствует ожидаемому:

openssl rsa -in rsa_private_key.pem -noout -text

Открытый ключ

Преобразовать открытый ключ из X.509 SubjectPublicKeyInfo в PKCS1:

PublicKey pub = pair.getPublic();
byte[] pubBytes = pub.getEncoded();

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

Преобразовать открытый ключ в PKCS1 в 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();

Проверьте с помощью командной строки OpenSSL, что формат ключа соответствует ожидаемому:

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

Спасибо

Большое спасибо авторам следующих постов:

Эти посты содержали полезную, хотя иногда и устаревшую информацию (например, для более старых версий BouncyCastle), которая помогла мне создать этот пост.

Начиная с RFC5208, незашифрованный формат PKCS#8 состоит из PrivateKeyInfo состав:

PrivateKeyInfo:: = SEQUENCE {
  версия версия,
  privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
  privateKey                PrivateKey,
  attribute [0]  IMPLICIT Attributes НЕОБЯЗАТЕЛЬНО}

где privateKey является:

"... строка октетов, содержимое которой является значением закрытого ключа. Интерпретация содержимого определяется при регистрации алгоритма закрытого ключа. Например, для закрытого ключа RSA содержимое представляет собой кодировку BER значение типа RSAPrivateKey."

это RSAPrivateKey Структура - это просто код ключа PKCS#1, который мы можем извлечь с помощью 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.

Я написал программу на C для преобразования закрытого ключа pkcs8 в pkcs1. Оно работает!

/*****************************************
    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;
}

Платформа BouncyCastle имеет кодер PKCS1 для решения этой проблемы: http://www.bouncycastle.org/docs/docs1.6/index.html

Я пытался сгенерировать открытые для OpenSSL открытые ключи RSA в формате DER, используя библиотеку BountyCastle J2ME, портированную на BlackBerry, мой код:

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();
}

Ключ все еще был неверным:

$ 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     

Я изменил org.bouncycastle.asn1.x509.AlgorithmIdentifier

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

И теперь есть хороший ключ:

$ 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 

Который может быть использован для шифрования:

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

Я знаю, что это старый пост. но я потратил два дня, чтобы решить эту проблему и, наконец, найти BouncyCastle может сделать это

ASN1Encodable

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

Другие вопросы по тегам