0

I have a RSA private key .der file (with a bunch of binary in it). How do I convert the file to PEM format in Java?

If I were to use openssl in command line, this is what I would do:

openssl rsa -inform der -in privateKey.der -outform pem -out privateKey.pem

The privateKey.pem result looks like this:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyljm4IGV2JmL8amyTQz9nCSdtdJV+OGiiyefJasKuNEIVAyV
BzSNEUrJYL1LrAMM0cbenWh3M0I+RoQYfwo3J88fi/aLW5dbdU8bi001UZIeJnEB
...
...
lykp1jNHSXD8PoBNVD+pbY3zGCqH1OgPWd0/+IbMBp3Qo+HMp5Ku9w==
-----END RSA PRIVATE KEY-----

I heard that people say PEM is just Base64-encoded representation of DER plus certain header and footer. However, if I just do cat privateKey.der | base64

I get different results from what openssl gave me: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKWObggZXYmYvxqbJNDP2 ... ... GndCj4cynkq73

Despite the head & footer, the PEM string is also not the same. Does anyone know why?

Are there any library to convert DER to PEM in Java like what openssl does? Been searching for a while but unfortunately, I didn't find anything that works. Any help would be greatly appreciated!

Yang
  • 1
  • 2

2 Answers2

2

There are many PEM formats, and OpenSSL supports about a dozen PEM and DER formats for private keys. The format written by openssl rsa [-outform pem] is the algorithm-specific PKCS1 appendix A format, with or without rfc1421-based encryption, and is labelled RSA PRIVATE KEY. The format written by openssl pkey or openssl pkcs8 -topk8 is the algorithm-generic PKCS8 format, which defines its own encryption, and is accordingly labelled PRIVATE KEY or ENCRYPTED PRIVATE KEY -- note no RSA in either case; see rfc7468. Java crypto e.g. PrivateKey.getEncoded() uses PKCS8 unencrypted, so if you got this DER from Java and converted it to base64 that's why you 'get different results'. Also note PEM is base64 of DER with line breaks every 64 chars plus header and footer; without the line breaks it won't always work. For both of these see PKCS#1 and PKCS#8 format for RSA private key .

Your answer using BouncyCastle is a good solution if you have or can get Bouncy; bcpkix correctly handles most OpenSSL formats. If you want to do it without Bouncy, it is nearly the same as How to encrypt RSA private key with PBE in PKCS#5 format in Java with IAIK JCE? (mine) except without encryption, so leave out the middle block and change the PEM header slightly:

    // given [RSA]PrivateKey privkey, get the PKCS1 part from the PKCS8 encoding
    byte[] pk8 = privkey.getEncoded();
    // this is wrong for RSA<=512 but those are totally insecure anyway
    if( pk8[0]!=0x30 || pk8[1]!=(byte)0x82 ) throw new Exception();
    if( 4 + (pk8[2]<<8 | (pk8[3]&0xFF)) != pk8.length ) throw new Exception();
    if( pk8[4]!=2 || pk8[5]!=1 || pk8[6]!= 0 ) throw new Exception();
    if( pk8[7] != 0x30 || pk8[8]==0 || pk8[8]>127 ) throw new Exception();
    // could also check contents of the AlgId but that's more work
    int i = 4 + 3 + 2 + pk8[8];
    if( i + 4 > pk8.length || pk8[i]!=4 || pk8[i+1]!=(byte)0x82 ) throw new Exception();
    byte[] old = Arrays.copyOfRange (pk8, i+4, pk8.length);
    
    // write to PEM format (substitute other file if desired)
    System.out.println ("-----BEGIN RSA PRIVATE KEY-----");
    String b64 = Base64.getEncoder().encodeToString(old);
    for( int off = 0; off < b64.length(); off += 64 )
        System.out.println (b64.substring(off, off+64<b64.length()?off+64:b64.length()));
    System.out.println ("-----END RSA PRIVATE KEY-----");

I'd think carefully before calling this 'better' though. This is also similar to Convert a X509 Public key to RSA public key for publickey (also mine) and the reverse of Reading a PKCS#1 or SPKI public key in Java without libraries .

Community
  • 1
  • 1
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
0

After lots of digging... I found a way, but it looks redundant. Please let me know if you have better idea.

    // My private key DER file.
    byte[] privateKeyDerBytes = Files.readAllBytes(Paths.get("tst/resource/FromSecretsManager.der"));
    
    // Convert the DER file to a Java PrivateKey
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyDerBytes);
    KeyFactory factory = KeyFactory.getInstance("RSA");
    PrivateKey pk = factory.generatePrivate(spec);

    // Use PemWriter to write the key to PEM format
    StringWriter sw = new StringWriter();
    try (PemWriter pw = new PemWriter(sw)) {
        PemObjectGenerator gen = new JcaMiscPEMGenerator(pk);
        pw.writeObject(gen);
    }
    String privateKeyPem = sw.toString();
Yang
  • 1
  • 2