Генерация ключей 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
Спасибо
Большое спасибо авторам следующих постов:
- /questions/3630354/generatsiya-klyuchej-rsa-v-formate-pkcs1-v-java/3630368#3630368
- /questions/42637624/preobrazovanie-otkryitogo-klyucha-v-formate-subjectpublickeyinfo-v-format-rsapublickey-java/42637642#42637642
- /questions/42637624/preobrazovanie-otkryitogo-klyucha-v-formate-subjectpublickeyinfo-v-format-rsapublickey-java/42637638#42637638
Эти посты содержали полезную, хотя иногда и устаревшую информацию (например, для более старых версий 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