5

I have a PKCS#1-formatted private key (generated by opendkim-genkey) like this

-----BEGIN RSA PRIVATE KEY-----

Base64 encoded data

-----END RSA PRIVATE KEY-----

Now I have to use it in Java to generate java.security.PrivateKey

But Java only support PKCS#8-formatted private key.

I know there is a way to convert from PKCS#8 to PKCS#1 by Java (using Bouncycastle), but is there anyway convert from PKCS#1 to PKCS#8 by Java?

Hash
  • 4,647
  • 5
  • 21
  • 39
Thanh Vũ
  • 55
  • 1
  • 5
  • You can do so using openssl with a command like `openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in pkcs1.key -out pkcs8.key`. Compare [here](https://stackoverflow.com/questions/8290435/) – Ben May 09 '18 at 10:03
  • Thank you, I know that I can do by using openssl command line. But what I ask is anyway we can do it by java. – Thanh Vũ May 09 '18 at 10:10
  • Why? You only have to do this process every few years when you re-key. Why write new Java code when an existing utility already does it correctly? – user207421 May 09 '18 at 10:19
  • Do (did) you need to _convert_ it or only to _use_ it? Those aren't the same. To _use_ a PKCS1 RSAPrivateKey (aka OpenSSL traditional clear) in Java with BouncyCastle see https://stackoverflow.com/questions/41934846/read-rsa-private-key-of-format-pkcs1-in-java . – dave_thompson_085 Aug 29 '22 at 01:23

2 Answers2

2

Converting a PKCS#1 encoded RSA private key to PKCS#8 that's consumable by vanilla Java is possible using Bouncy Castle's bcprov library.

Since the difference between PKCS#1 and PKCS#8 is simply the leading PrivateKeyAlgorithmIdentifier, we can supplement that to get PKCS#8:

byte[] pkcs1Encoded = ...;

AlgorithmIdentifier algId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
PrivateKeyInfo privateKeyInfo = new PrivateKeyInfo(algId, ASN1Sequence.getInstance(pkcs1Encoded));

byte[] pkcs8Encoded = privateKeyInfo.getEncoded();

This is then consumable by vanilla Java:

java.security.spec.KeySpec spec = new java.security.spec.PKCS8EncodedKeySpec(pkcs8Encoded);
java.security.KeyFactory.getInstance("RSA").generatePrivate(spec);

However, if you're using Bouncy Castle anyway, you can use their provider and you don't need to explicitly convert PKCS#1 to PKCS#8 since their KeyFactorySpi can consume a PKCS8EncodedKeySpec with a PKCS#1 shoved into it.

e.g.:

java.security.spec.KeySpec spec = new java.security.spec.PKCS8EncodedKeySpec(pkcs1Encoded);
java.security.KeyFactory.getInstance("RSA", new BouncyCastleProvider()).generatePrivate(spec);

The Bouncy Castle provider can also be registered globally:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

...

java.security.spec.KeySpec spec = new java.security.spec.PKCS8EncodedKeySpec(pkcs1Encoded);
java.security.KeyFactory.getInstance("RSA").generatePrivate(spec);
antak
  • 19,481
  • 9
  • 72
  • 80
0

Disclaimer: I did not come up with this solution myself, it was written by marcoscottwright over at github. Find the original code here


You can do so using BouncyCastle given you have a PrivateKey k object.

try (ASN1InputStream asn1InputStream = new ASN1InputStream(k.getEncoded()))
{
    DERObject rsaPrivateKey = asn1InputStream.readObject();
    return new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption), rsaPrivateKey).getDEREncoded();
}
Ben
  • 1,665
  • 1
  • 11
  • 22