# Using openssl and java for RSA keys

Monday, August 29, 2016 •

If you want to use public key encryption, you’ll need public and private keys in some format. OpenSSL and many other tools can generate such key pairs as well as java. However, if it comes to interoperability between these tools, you’ll need to be a bit careful. This post shows, how to generate a key pair with openssl, store it in files and load these key pairs in Java for usage.

## Part 1: Generate a fresh key pair with openssl

You start with generating a private key using the `genrsa`

tool from OpenSSL:

```
openssl genrsa -out privatekey.pem 2048
```

This creates a new RSA private key with 2048 bits length. The key is stored in the file `privatekey.pem`

and
it is in the “PEM” format. The PEM format is essentially a base64-encoded variant of a DER-encoded structure. You
can look at the file, it should start with an “BEGIN RSA PRIVATE KEY” header and end with “END RSA PRIVATE KEY” footer:

```
head -2 privatekey.pem; tail -1 privatekey.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAth6P/MXUGL1y69Ao9THV16taSeUWnM4FQpmHP0yMDS3hB4V0
-----END RSA PRIVATE KEY-----
```

This file is now all we need to get started. Although this seems just to be the private key and the public key seems to be missing - it is not: This private key format contains all the information to reconstruct the public key data.

## Part 2: Extract the public key

The second tool from OpenSSL we’ll use is `rsa`

. This allows to do some conversions of the key files.

```
openssl rsa -in privatekey.pem -out publickey.pem -pubout
```

If we look at the generate file `publickey.pem`

, we see, that is also in the PEM format. The header and footer
lines are “BEGIN PUBLIC KEY” and “END PUBLIC KEY” respectively:

```
head -2 publickey.pem; tail -1 publickey.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAth6P/MXUGL1y69Ao9THV
-----END PUBLIC KEY-----
```

Now we have the plain key files available. You could distribute the public key file to allow the other party to
encrypt some data while keeping the private key save. Please note, that the private key file is **not** encrypted
and must be secured in some way (like file permissions, etc.).

## Part 3: Understanding the key files structure

Java itself cannot directly load the PEM files generated in the above steps. However, the PEM files are actually just the “DER” format base 64 encoded with the additional headers and footers. But what is the “DER” format? The man page explains a bit:

`-inform DER|NET|PEM This specifies the input format. The DER option uses an ASN1 DER encoded form compatible with the PKCS#1 RSAPrivateKey or SubjectPublicKeyInfo format. The PEM form is the default format: it consists of the DER format base64 encoded with additional header and footer lines. On input PKCS#8 format private keys are also accepted. The NET form is a format is described in the NOTES section.`

So there is the standard PKCS#1 which defines the structures `RSAPrivateKey`

and `SubjectPublicKeyInfo`

.
The standard has been published also as RFC 3447. The recommendation for
interchanging private keys is described in appendix A.1.2:

`RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL }`

You can see two things: The structure is basically an list of numbers. And the private key structure contains the modulus - that’s the reason why you can extract the public key from this private key file.

The public key structure `SubjectPublicKeyInfo`

is described in appenx A.1.1:

`RSAPublicKey ::= SEQUENCE { modulus INTEGER, -- n publicExponent INTEGER -- e }`

You can display this info in “human readable” format using OpenSSL, too:

```
openssl rsa -in privatekey.pem -text
Private-Key: (2048 bit)
modulus:
00:b6:1e:8f:fc:c5:d4:18:bd:72:eb:d0:28:f5:31:
...
publicExponent: 65537 (0x10001)
privateExponent:
00:a9:f4:cb:9a:b1:63:c5:d2:c6:b4:9a:86:1e:8c:
... It will display all the fields. The same is possible with the public key:
openssl rsa -in publickey.pem -text -pubin
Public-Key: (2048 bit)
Modulus:
00:b6:1e:8f:fc:c5:d4:18:bd:72:eb:d0:28:f5:31:
...
```

## Part 4: Converting the key files for usage in Java (Public Key)

Plain Java is able to understand the public key format. However, it can’t read the PEM file directly, but it can understand the DER encoding. The solution is, to decode the file first using Base64 and then let it parse by Java. Here’s a snippet that does this:

```
public static PublicKey loadPublicKey() throws Exception {
String publicKeyPEM = FileUtils.readFileToString(new File("publickey.pem"), StandardCharsets.UTF_8);
// strip of header, footer, newlines, whitespaces
publicKeyPEM = publicKeyPEM
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s", "");
// decode to get the binary DER representation
byte[] publicKeyDER = Base64.getDecoder().decode(publicKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyDER));
return publicKey;
}
```

It uses `X509EncodedKeySpec`

to load the public key, which is just the recommended `SubjectPublicKeyInfo`

implementation.

Note: I used Apache’s commons-io library for `FileUtils`

. Everything
else is include in the standard Java8 JDK.

## Part 5: Convert the private key for for usage in Java

Unfortunately we can’t use the exact same trick for the private key. Java has an encoded key spec for the private
key: `PKCS8EncodedKeySpec`

- however, it implements “PKCS#8” rather than “PKCS#1” that we used. Luckily,
OpenSSL contains also a converter for this format:

```
openssl pkcs8 -in privatekey.pem -topk8 -nocrypt -out privatekey-pkcs8.pem
```

If you inspect the generated file, you’ll see again the PEM format, but now with the header “BEGIN PRIVATE KEY”:

```
head -2 privatekey-pkcs8.pem; tail -1 privatekey-pkcs8.pem
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2Ho/8xdQYvXLr
-----END PRIVATE KEY-----
```

Please note, that this private key file is also not encrypted (`nocrypt`

) and must be kept safe.

This format is described in RFC 5208 and the structure in section 5:

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

It is again DER encoded and it is actually just wrapping the `RSAPrivateKey`

structure from above in
the `privateKey`

field. However, this format allows for encrypting the private key with a password
(which we don’t use in this example).

Now we can load the private key in Java, too:

```
public static PrivateKey loadPrivateKey() throws Exception {
String privateKeyPEM = FileUtils.readFileToString(new File("privatekey-pkcs8.pem"), StandardCharsets.UTF_8);
// strip of header, footer, newlines, whitespaces
privateKeyPEM = privateKeyPEM
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
// decode to get the binary DER representation
byte[] privateKeyDER = Base64.getDecoder().decode(privateKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyDER));
return privateKey;
}
```

## Part 6: Encrypting and Decrypting in Java using RSA

Now we can use Java to encrypt and decrypt like this:

```
public static void main(String[] args) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
String clearText = "Sample plain text";
PublicKey publicKey = loadPublicKey();
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8));
PrivateKey privateKey = loadPrivateKey();
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("ClearText: " + clearText);
System.out.println("Decrypted: " + new String(decrypted, StandardCharsets.UTF_8));
System.out.println("ClearText length: " + clearText.getBytes(StandardCharsets.UTF_8).length);
System.out.println("Encrypted length: " + encrypted.length);
System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
}
```

The output looks like this:

```
ClearText: Sample plain text
Decrypted: Sample plain text
ClearText length: 17
Encrypted length: 256
Encrypted: riHHycTvKaDtX3SkeoZbFCW3KW3vxEIsF3wVQqOKuwAbTtWFyP6yN5essem+jTx16Ggdp6/rzS9r9Wy5O6P8JuOQAKi...
```

You can see that the clear text has been bloated up to 256 bytes. This is becaue I generated a RSA key with 2048 bits length, which are 256 bytes. The RSA cipher encrypts in blocks and uses padding in the blocks. I used “PKCS1Padding” which uses 11 bytes for padding, which means, you can at most encrypt 256-11=245 bytes of plain data in one block. If you have bigger data to encrypt, you’ll need to chain these blocks. There are different ways to chain blocks: Electronic Codebook (ECB), Cipher Block Chaining (CBC). See Block cipher mode of operation. You could also consider using a hybrid method, which means, you’ll exchange a symmetric key for AES via RSA first and then use this AES key for the bigger data you want to exchange.