I am creating one library for encryption/decryption using AES-256 with GCM Mode(With Random IV/Random Salt) for every request.
Code I have written is(reference : AES Encryption/Decryption with key) :
public class AESGCMChanges {
static String plainText1 = "DEMO text to be encrypted @1234";
static String plainText2 = "999999999999";
public static final int AES_KEY_SIZE = 256;
public static final int GCM_IV_LENGTH = 12;
public static final int GCM_TAG_LENGTH = 16;
public static final int GCM_SALT_LENGTH = 32;
private static final String FACTORY_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final String KEY_ALGORITHM = "AES";
private static final int KEYSPEC_ITERATION_COUNT = 65536;
private static final int KEYSPEC_LENGTH = 256;
private static final String dataKey = "demoKey";
public static void main(String[] args) throws Exception
{
byte[] cipherText = encrypt(plainText1.getBytes());
String decryptedText = decrypt(cipherText);
System.out.println("DeCrypted Text : " + decryptedText);
cipherText = encrypt(plainText2.getBytes());
decryptedText = decrypt(cipherText);
System.out.println("DeCrypted Text : " + decryptedText);
}
public static byte[] encrypt(byte[] plaintext) throws Exception
{
byte[] IV = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] salt = generateSalt();
// Generate Key
SecretKey key = getDefaultSecretKey(dataKey, salt);
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
byte[] message = new byte[GCM_SALT_LENGTH + GCM_IV_LENGTH + plaintext.length + GCM_TAG_LENGTH];
System.arraycopy(salt, 0, message, 0, GCM_SALT_LENGTH);
System.arraycopy(IV, 0, message, GCM_SALT_LENGTH, GCM_IV_LENGTH);
System.arraycopy(cipherText, 0, message, GCM_SALT_LENGTH+GCM_IV_LENGTH, cipherText.length);
return message;
}
public static String decrypt(byte[] cipherText) throws Exception
{
if (cipherText.length < GCM_IV_LENGTH + GCM_TAG_LENGTH + GCM_SALT_LENGTH) throw new IllegalArgumentException();
ByteBuffer buffer = ByteBuffer.wrap(cipherText);
// Get Salt from Cipher
byte[] salt = new byte[GCM_SALT_LENGTH];
buffer.get(salt, 0, salt.length);
// GET IV from cipher
byte[] ivBytes1 = new byte[GCM_IV_LENGTH];
buffer.get(ivBytes1, 0, ivBytes1.length);
byte[] encryptedTextBytes = new byte[buffer.capacity() - salt.length - ivBytes1.length];
buffer.get(encryptedTextBytes);
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Generate Key
SecretKey key = getDefaultSecretKey(dataKey, salt);
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, ivBytes1);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(encryptedTextBytes);
return new String(decryptedText);
}
private static SecretKey getDefaultSecretKey(final String password, final byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException{
return getSecretKey(password, salt, FACTORY_ALGORITHM, KEY_ALGORITHM, KEYSPEC_ITERATION_COUNT, KEYSPEC_LENGTH);
}
private static SecretKey getSecretKey(final String password,
final byte[] salt,
final String factoryAlgorithm,
final String keyAlgorithm,
final int iterationCount,
final int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException{
SecretKeyFactory factory = SecretKeyFactory.getInstance(factoryAlgorithm);
return new SecretKeySpec(factory.generateSecret(new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength)).getEncoded(), keyAlgorithm); //thanks alot for the bug report
}
private static byte[] generateSalt()
{
final Random r = new SecureRandom();
byte[] salt = new byte[32];
r.nextBytes(salt);
return salt;
}
}
Now my question is:
- Does it have any security flaws?
- Does it follow best practices?
- Can I do something to improve it?
All the lengths that I have taken for SALT, IV, Authentication tag are they OK? or they needs to be changed.