RSA encryption in Java (ctd)

On the previous page, we introduced RSA encryption, a form of asymmetric encryption. We saw how to generate an RSA key pair in Java. The key pair consisted of a public key that we gave to all clients, and a private key that we kept secret on the server. Thus, any client can generate a message that our server— and only our server (or somebody who has access to the private key)— can decrypt.

Encryption

The Java code to perform RSA encryption uses a Cipher object, just as with AES encryption. However, this time, we want to initialise the cipher with the public key that we previously saved to file. We use a KeyFactory as before, reversing the process we went through when saving the key. So we'll pull out the two BigIntegers from the file, wrap an RSAPublicKeySpec object around them, and run that through a KeyFactory instance to generate a corresponding Key object:

PublicKey readKeyFromFile(String keyFileName) throws IOException {
  InputStream in =
    ServerConnection.class.getResourceAsStream(keyFileName);
  ObjectInputStream oin =
    new ObjectInputStream(new BufferedInputStream(in));
  try {
    BigInteger m = (BigInteger) oin.readObject();
    BigInteger e = (BigInteger) oin.readObject();
    RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PublicKey pubKey = fact.generatePublic(keySpec);
    return pubKey;
  } catch (Exception e) {
    throw new RuntimeException("Spurious serialisation error", e);
  } finally {
    oin.close();
  }
}

Then, we can use this PublicKey object to initialise a Cipher and encrypt some data. Note that this time, we specify RSA as the required cipher algorithm. Then, we proceed as with symmetric encryption: we initialise the cipher in encryption mode with our key (in this case, the RSA public key), and call doFinal(), passing in the data to be encrypted:

public byte[] rsaEncrypt(byte[] data) {
  PublicKey pubKey = readKeyFromFile("/public.key");
  Cipher cipher = Cipher.getInstance("RSA");
  cipher.init(Cipher.ENCRYPT_MODE, pubKey);
  byte[] cipherData = cipher.doFinal(src);
  return cipherData;
}

The resulting cipherData is the encrypted data and can be safely sent over the wire to the client.

Decryption

The server goes through a very similar process, and the code is trivially similar to the client's. The server reads in its modulus and exponent from its private key file and constructs a corresponding RSAPrivateKeySpec object, using a KeyFactory to translate it into a PrivateKey. This PrivateKey can then be used to initialise a Cipher in DECRYPT_MODE.

Next: key sizes

We now have more or less all the information we need to use RSA encryption. However, one important issue that we've glossed over so far is key length. On the next page, we'll discuss RSA key length and how to choose an appropriate size.


If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.