9

I have an unencrypted PKCS8 encoded file that represents a Private Key. It can be any of these private key types - RSA, DSA or EC. I viewed these files in an ASN1 decoder (https://lapo.it/asn1js/) and I could see the type (RSA, DSA or EC) in the data.

Is there a way to read the PKC8 private key data into the correct Private Key Java object without specifying the key type in code like this -

PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
KeyFactory factory = KeyFactory.getInstance("RSA"); // Avoid "RSA" here?
PrivateKey privateKey = factory.generatePrivate(spec);

Is there a way to avoid specifying the algorithm in KeyFactory.getInstance("RSA")? Shouldn't this be determined from the PKCS8EncodedKeySpec since it is available in the PKCS8 data?

Sample unencrypted PKCS8 data and their ASN1 decodings which show the key type -

DSA - link

EC - link

RSA - link

MediumOne
  • 804
  • 3
  • 11
  • 28
  • According to https://docs.oracle.com/javase/7/docs/api/java/security/spec/PKCS8EncodedKeySpec.html it looks like there is a `PrivateKeyInfo` description in the key/object, which contains the algorithm used. Use it to load the correct `KeyFactory` instance. – Progman Aug 21 '18 at 17:33
  • So you mean to say that PKCS8EncodedKeySpec.getEncoded() returns the ASN1 encoded data that contains the PrivateKey algorithm? I couldn't find any class called `PrivateKeyInfo` in the JDK classes to load this data into (i.e., the data returned by `getEncoded()`). That's where I need help. How do I read this private key data and get the algorithm type using generic Java APIs without coding specifically for RSA/DSA/EC? – MediumOne Aug 22 '18 at 09:03
  • Downvoter, please explain the reason for downvote. If the question is not clear, let me know and I will re-phrase. Let's say you stumble across a PEM encoded unencrypted private key file (note that this data contains the PrivateKey algorithm). How would you write code to read this data and get a PrivateKey instance without specifying the Private Key algorithm type in the code? I think this is a valid question. – MediumOne Aug 22 '18 at 09:06
  • 2
    https://www.programcreek.com/java-api-examples/?api=org.bouncycastle.asn1.pkcs.PrivateKeyInfo looks promising and it's the first google hit. You can try to adapt it for your code. – Progman Aug 22 '18 at 15:10
  • Thanks. That link helps. I was not expecting the use of BouncyCastle library (which I don't use currently), but this should work for me. If you can post the code from the link as an answer, I can accept it as a valid answer. Thanks again. – MediumOne Aug 24 '18 at 14:38
  • You can add an answer and accept it by yourself. I will not provide an answer because I haven't tested it, hence only providing the link (which might get deleted). But since you have tested it and you might have a working code base, you can add the answer to this question. – Progman Aug 24 '18 at 16:41

1 Answers1

6

This can be achieved with the help of BouncyCastle APIs -

/** Read a PKCS#8 format private key. */
private static PrivateKey readPrivateKey(InputStream input)
throws IOException, GeneralSecurityException {
    try {
        byte[] buffer = new byte[4096];
        int size = input.read(buffer);
        byte[] bytes = Arrays.copyOf(buffer, size);
        /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        /*
         * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
         * OID and use that to construct a KeyFactory.
         */
        ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
        PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
        String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
        return KeyFactory.getInstance(algOid).generatePrivate(spec);
    } finally {
        input.close();
    }
}
MediumOne
  • 804
  • 3
  • 11
  • 28
  • 1
    Thank you for this useful answer! After your snippet I found even a way to simplify a bit more. `PrivateKey privateKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(privateKeyInfo);` I thought it would be to handy to share it here. In this way the last two lines regarding the algorithmId and KeyFactory can be omitted – Hakan54 Sep 13 '20 at 17:10
  • Does this work? As far as I know you can't feed a KeyFactory.getInstance() with a OID but you need to supply the name of the provider such as RSA or EC? – Jasper Siepkes Apr 21 '21 at 08:16
  • Ah right the default Java provider does not take OID's but the Bouncy Castle provider does (using `KeyFactory.getInstance(algOid, "BC");`). – Jasper Siepkes Apr 21 '21 at 08:23