Генерация ключей RSA в формате PKCS # 1 в Java - PullRequest
21 голосов
/ 30 сентября 2011

Когда я генерирую пару ключей 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 ]

22 голосов
/ 27 апреля 2015

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

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

Преобразование открытого ключа из SubjectPublicKeyInfo X.509 в 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), которая помогла мне создать этот пост.

6 голосов
/ 03 января 2012

С RFC5208 , незашифрованный формат PKCS # 8 состоит из PrivateKeyInfo структуры:

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

, где 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.
0 голосов
/ 17 декабря 2014

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

ASN1Encodable

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

0 голосов
/ 29 января 2013

Я написал программу на 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;
}
0 голосов
/ 03 января 2012

Я пытался сгенерировать открытые для 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
0 голосов
/ 30 сентября 2011

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

...