In a previous tutorial we dealt with RSA key generation and how to save them to PEM files in Java. In this tutorial we will learn how to read RSA keys from PEM files that we previously created. Once again, we will make use of the BouncyCastle library to take benefit from its PEM related classes, such as PemObject and PemReader.

To deal with this tutorial it is required:

  • Maven.
  • JDK 1.6+
  • An IDE, such as Eclipse or Spring Tool Suite.

Table of Contents

1. Create a Maven project

Having Maven installed and its environment variables properly configured, execute the following command in a shell to create a brand new Maven project:

mvn archetype:generate -DgroupId=com.txedo.security -DartifactId=bouncycastle-pem-read -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Import the project into your favourite IDE and let’s get down to work.

2. Configure the POM file

Although we could have just evolved the bouncycastle-rsa-pem-write project, we have opted for creating a new one to add the new functionality. I suggest you as homework to merge both projects together. That said, the first step is to edit the pom file and add the BouncyCastle dependency. In the snippet below, we also added the log4j dependency to print some debug traces in our program.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.txedo.security</groupId>
  <artifactId>bouncycastle-pem-read</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <dependencies>
    <dependency>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcprov-jdk15on</artifactId>
      <version>1.51</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
  </dependencies>
</project>
  
pom.xml

3. Create PemFile class to encapsulate PEM file I/O operations

Create the PemFile class and add a constructor that accepts a string as argument to initialize a PemObject attribute (line 14). The argument must be the full path and filename to a PEM file located anywhere in your local hard drive or classpath. Do not forget to close the reader after successfully or unsuccessfully performing the reading operation to avoid warnings or unexpected behavior (line 18). Later, the main program will create PemFile instances and actually read RSA keys.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

public class PemFile {

  private PemObject pemObject;

  public PemFile(String filename) throws FileNotFoundException, IOException {
    PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream(filename)));
    try {
      this.pemObject = pemReader.readPemObject();
    } finally {
      pemReader.close();
    }
  }

  public PemObject getPemObject() {
    return pemObject;
  }
}
  
PemFile.java

4. Create the main program class

The main class have two key generation methods, one for the private key and another for the public key that actually read RSA keys, and the main method that orchestrates all together.

First, the generate private key method (line 38) takes the filename to instantiate a PemFile instance and read the private RSA key, build a PKCS8EncodedKeySpec and then generate the private key. The generate public key method (line 45) takes seamlessly the filename to instantiate a PemFile instance and read the public RSA key, build a X509EncodedKeySpec and then generate the public key.

Finally, the main method adds the BouncyCastle provider (line 23), gets a key factory instance using such provider (line 26) and generates previously read RSA keys by calling the generation methods described above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class Main {

  protected final static Logger LOGGER = Logger.getLogger(Main.class);
  
  public final static String RESOURCES_DIR = "src/main/resources/rsa-sample/";

  public static void main(String[] args) throws FileNotFoundException, IOException, NoSuchAlgorithmException, NoSuchProviderException {
    Security.addProvider(new BouncyCastleProvider());
    LOGGER.info("BouncyCastle provider added.");

    KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
    try {
      PrivateKey priv = generatePrivateKey(factory, RESOURCES_DIR + "id_rsa");
      LOGGER.info(String.format("Instantiated private key: %s", priv));
      
      PublicKey pub = generatePublicKey(factory, RESOURCES_DIR + "id_rsa.pub");
      LOGGER.info(String.format("Instantiated public key: %s", pub));
    } catch (InvalidKeySpecException e) {
      e.printStackTrace();
    }
  }

  private static PrivateKey generatePrivateKey(KeyFactory factory, String filename) throws InvalidKeySpecException, FileNotFoundException, IOException {
    PemFile pemFile = new PemFile(filename);
    byte[] content = pemFile.getPemObject().getContent();
    PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
    return factory.generatePrivate(privKeySpec);
  }
  
  private static PublicKey generatePublicKey(KeyFactory factory, String filename) throws InvalidKeySpecException, FileNotFoundException, IOException {
    PemFile pemFile = new PemFile(filename);
    byte[] content = pemFile.getPemObject().getContent();
    X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
    return factory.generatePublic(pubKeySpec);
  }
}
  
Main.java

5. Result

Once the program is executed, we get a console output like this:

Main:27 - BouncyCastle provider added.
Main:34 - Instantiated private key: RSA Private CRT Key
            modulus: 80bfe9b773b36733509054642c2b5ab9f6ed3bb7f05d891b53dc04b9197c3bc0fb989dd3b9fff74ec53e38911089173c9507db1ed237f105e09e7de8b07dd1ec7c1c4859a80586fa909b8c7e3bfac90a696b51416d8d9b0774ffe3ffe494f36f5063e0f75b12772d7397ac297b2f45cd579b0fc24f36f206c8a8f4d07f2fe4dd
    public exponent: 10001
   private exponent: 67ff9d806c5b3c3f7d1238b8f9dcc35a78154529dd6510bd1c4e1b1a917582a6ee14675881643b964b496953f471686dad4c3d3976416dd57b8d4ad34ccd35658a8ffdabe54c349034777a0540b803baaa0274a2ff9ae188920789c88a27cb1973ff3a54c3995583029cc560764b4bb49bf33850d6960f0aed5148b15d69aa59
             primeP: ef2b3fbb698e696a42bd5868cc1af071243ac93c21f770b835350c990d8fca2d0c36f4a00389356cf34aa4d23bd9717b0475eb7f46b69da1ad7742096b6aef7f
             primeQ: 89cf6770a0abd13425b670d678814d9a826f5b02e2c6696dff6355fc0431ece44cef4b29985d72c928200938881dacf05ca4bd2e4109a3f58d1b362c924a19a3
     primeExponentP: 9d3bb499740ac0f8afc9a52eb848599c3832418bbbd4dd90ecc1e47756781a75451b77f51e7dfcd694979505f57cbd631f8a9a78c1375b28284f47e5c36db8e7
     primeExponentQ: 553020813ed0f741850e823211cbdc6ce6b46f4e19610d4b31d3f6131384c92b576394d2e19ce297f675d73d5ae6fd4098043ded99c69bd3eea62396e9d76481
     crtCoefficient: daac44c1e731d792eabef504d577622b578082651bae4fa0c1e58579aaac6f64f9933f363252ccf2689bf0b981cf976ee41030730a8a8bc8a059aff86976a1fe

Main:37 - Instantiated public key: RSA Public Key
            modulus: 80bfe9b773b36733509054642c2b5ab9f6ed3bb7f05d891b53dc04b9197c3bc0fb989dd3b9fff74ec53e38911089173c9507db1ed237f105e09e7de8b07dd1ec7c1c4859a80586fa909b8c7e3bfac90a696b51416d8d9b0774ffe3ffe494f36f5063e0f75b12772d7397ac297b2f45cd579b0fc24f36f206c8a8f4d07f2fe4dd
    public exponent: 10001

The output indicates that the program read successfully the id_rsa and id_rsa.pub PEM files and printed them out in terms of modulus and exponents.

Download the code

The code used in this article to read RSA keys from PEM files in Java can be found in GitHub:

References

  1. The Legion of the Bouncy Castle