I spent an evening recently playing around with cryptography in Java. I had to look up something similar for work and then it piqued my interest so I had a bit of a play with it.
What I was looking to do was AES encrypt a file using a key derived from a passphrase. After some digging around it seems that the way to create a key is to use the PKCS #5 algorithm and hash the result. This can be done in Java with a key factory using the algorithm PBKDF2WithHmacSHA1:
private static SecretKey generatePBKKey(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
rand.nextBytes(salt);
KeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 2, 128);
SecretKey generatedKey = keyFactory.generateSecret(pbeKeySpec);
SecretKey encKey = new SecretKeySpec(generatedKey.getEncoded(), "AES");
return encKey;
}
This key can then be used to initialise a AES Cipher which you can then encrypt the file with using a CipherOutputStream. Creating the AES Cipher was done using AES with ECB (Electronic Code Book) and PKCS5 padding.
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
I wasn't sure what block mode and padding to use and this seemed to be what most use.
The best doco I could find from Oracle was the Java 6 guide which ran through the main classes - http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html
The full code of what I ended up with is over the jump.
package neb;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Provider.Service;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoTest {
/**
* @param args
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws IOException
* @throws InvalidKeySpecException
*/
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IOException, InvalidKeySpecException {
for (Provider provider : Security.getProviders()) {
System.out.println("provider " + provider.getName());
for (Service service : provider.getServices()) {
System.out.println("sevice available " + service.getAlgorithm());
}
}
String password = "please7890123456";
Key key = generatePBKKey(password);
System.out.println("key " + key.getEncoded());
Cipher factoryEncryptCipher = createAndInitialiseCipherUsingPKCS(key, Cipher.ENCRYPT_MODE);
String fileName = "EncryptMe.txt";
String encryptedFileName = encrypt(fileName, factoryEncryptCipher);
Cipher factoryDecryptCipher = createAndInitialiseCipherUsingPKCS(key, Cipher.DECRYPT_MODE);
decrypt(encryptedFileName, factoryDecryptCipher);
}
private static String encrypt(String fileName, Cipher cipher) throws IOException {
File inputFile = new File(fileName);
FileInputStream fileInputStream = new FileInputStream(inputFile);
String returnFileName = fileName + "-encrypted.txt";
File outputFile = new File(returnFileName);
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher);
byte[] bytes = new byte[8];
int bytesRead = fileInputStream.read(bytes);
while (bytesRead != -1) {
cipherOutputStream.write(bytes, 0, bytesRead);
bytesRead = fileInputStream.read(bytes);
}
cipherOutputStream.flush();
cipherOutputStream.close();
fileInputStream.close();
return returnFileName;
}
private static String decrypt(String fileName, Cipher cipher) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, IOException {
File encryptedFile = new File(fileName);
FileInputStream fileInputStream = new FileInputStream(encryptedFile);
CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher);
String decryptedFileName = fileName + "-decrypted.txt";
File outputFile = new File(decryptedFileName);
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
byte[] bytes = new byte[8];
int bytesRead = cipherInputStream.read(bytes);
while (bytesRead != -1) {
fileOutputStream.write(bytes, 0, bytesRead);
bytesRead = cipherInputStream.read(bytes);
}
fileOutputStream.flush();
cipherInputStream.close();
fileOutputStream.close();
return decryptedFileName;
}
private static Cipher createAndInitialiseCipherUsingPKCS(Key key, int mode)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(mode, key);
return cipher;
}
private static SecretKey generatePBKKey(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
rand.nextBytes(salt);
KeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 2, 128);
SecretKey generatedKey = keyFactory.generateSecret(pbeKeySpec);
SecretKey encKey = new SecretKeySpec(generatedKey.getEncoded(), "AES");
return encKey;
}
private static Cipher createAndInitialiseCipherManualKey(String password, int mode)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException {
Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec secretKeySpec = new SecretKeySpec(password.getBytes(), "AES");
cipher.init(mode, secretKeySpec);
return cipher;
}
}
No comments:
Post a Comment