You are on page 1of 3

his 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 toolOpenSSL. 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);

You might also like