Lore Michael Lones

Encrypting files with Public Key Encryption in Java

This HOWTO describes one way of implementing public key encryption in Java. It is generally not advisable to use a public key encryption algorithm such as RSA to directly encrypt files, since (i) public key encryption is slow, and (ii) it will only let you encrypt small things (...well, I haven't managed to get it to encrypt big things ;)

The alternative, and commonly used approach, is to use a shared key algorithm to encrypt/decrypt the files, and then use a public key algorithm to encrypt/decrypt the (randomly generated) key used by the shared key algorithm. This has the benefit of fast file encryption/decryption whilst still requiring a non-shared private key to get access to the key needed to decrypt the files.

In this HOWTO, I use the RSA public key algorithm and the AES shared key algorithm. The code is here.

Note to Windows users: you will need to install Sun's Unlimited Strength Jurisdiction Policy Files to run this code - for the most recent java version, these are currently available here (the last item in the list). If you still get an InvalidKeyException, check that the policy files are installed in both the JDK and the JRE directories.

The Code

The encryption algorithms are specified in the constructor:

  public FileEncryption() throws GeneralSecurityException {
   
// create RSA public key cipher
   
pkCipher = Cipher.getInstance("RSA");
   
// create AES shared key cipher
   
aesCipher = Cipher.getInstance("AES");
 
}

A random AES key is generated to encrypt files. A key size (AES_Key_Size) of 256 bits is standard for AES:

  public void makeKey() throws NoSuchAlgorithmException {
   
KeyGenerator kgen = KeyGenerator.getInstance("AES");
    kgen.init
(AES_Key_Size);
    SecretKey key = kgen.generateKey
();
    aesKey = key.getEncoded
();
    aeskeySpec =
new SecretKeySpec(aesKey, "AES");
 
}

The file encryption and decryption routines then use the AES cipher:

  public void encrypt(File in, File out) throws IOException, InvalidKeyException {
   
aesCipher.init(Cipher.ENCRYPT_MODE, aeskeySpec);
   
    FileInputStream is =
new FileInputStream(in);
    CipherOutputStream os =
new CipherOutputStream(new FileOutputStream(out), aesCipher);
   
    copy
(is, os);
   
    os.close
();
 
}
 
 
public void decrypt(File in, File out) throws IOException, InvalidKeyException {
   
aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec);
   
    CipherInputStream is =
new CipherInputStream(new FileInputStream(in), aesCipher);
    FileOutputStream os =
new FileOutputStream(out);
   
    copy
(is, os);
   
    is.close
();
    os.close
();
 
}
 
 
private void copy(InputStream is, OutputStream os) throws IOException {
   
int i;
   
byte[] b = new byte[1024];
   
while((i=is.read(b))!=-1) {
     
os.write(b, 0, i);
   
}
  }

So that the files can be decrypted later, the AES key is encrypted to a file using the RSA cipher. The RSA public key is assumed to be stored in a file.

  public void saveKey(File out, File publicKeyFile) throws IOException, GeneralSecurityException {
   
// read public key to be used to encrypt the AES key
   
byte[] encodedKey = new byte[(int)publicKeyFile.length()];
   
new FileInputStream(publicKeyFile).read(encodedKey);
   
   
// create public key
   
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
    KeyFactory kf = KeyFactory.getInstance
("RSA");
    PublicKey pk = kf.generatePublic
(publicKeySpec);
   
   
// write AES key
   
pkCipher.init(Cipher.ENCRYPT_MODE, pk);
    CipherOutputStream os =
new CipherOutputStream(new FileOutputStream(out), pkCipher);
    os.write
(aesKey);
    os.close
();
 
}

Before decryption can take place, the encrypted AES key must be decrypted using the RSA private key:

  public void loadKey(File in, File privateKeyFile) throws GeneralSecurityException, IOException {
   
// read private key to be used to decrypt the AES key
   
byte[] encodedKey = new byte[(int)privateKeyFile.length()];
   
new FileInputStream(privateKeyFile).read(encodedKey);
   
   
// create private key
   
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
    KeyFactory kf = KeyFactory.getInstance
("RSA");
    PrivateKey pk = kf.generatePrivate
(privateKeySpec);
   
   
// read AES key
   
pkCipher.init(Cipher.DECRYPT_MODE, pk);
    aesKey =
new byte[AES_Key_Size/8];
    CipherInputStream is =
new CipherInputStream(new FileInputStream(in), pkCipher);
    is.read
(aesKey);
    aeskeySpec =
new SecretKeySpec(aesKey, "AES");
 
}

Usage

To use the code, you need corresponding public and private RSA keys. RSA keys can be generated using the open source tool OpenSSL. However, you have to be careful to generate them in the format required by the Java encryption libraries. To generate a private key of length 2048 bits:

openssl genrsa -out private.pem 2048

To get it into the required (PKCS#8, DER) format:

openssl pkcs8 -topk8 -in private.pem -outform DER -out private.der -nocrypt

To generate a public key from the private key:

openssl rsa -in private.pem -pubout -outform DER -out public.der

An example of how to use the code:

FileEncryption secure = new FileEncryption();

// to encrypt a file
secure.makeKey();
secure.saveKey
(encryptedKeyFile, publicKeyFile);
secure.encrypt
(fileToEncrypt, encryptedFile);

// to decrypt it again
secure.loadKey(encryptedKeyFile, privateKeyFile);
secure.decrypt
(encryptedFile, unencryptedFile);
These web pages use Google Analytics to track visits. See Privacy Statement.
© Michael Lones 2005-2016. Page last updated 30th October, 2013.