Java AES encryption and decryption with static secret

I have an application that needs to store some secret passwords in a configuration file, such as a database and ftp / detail passwords. I looked around and found many encryption / decryption solutions using AES, but I cannot figure out how to make it work without changing the key. This means that I can encrypt and decrypt (using the same SecretKey), but maintain a constant restart, etc. I can't get SecretKey to stay the same. The following example shows my working methods:

String secret = Encryptor.encrpytString("This is secret"); String test = Encryptor.decrpytString(secret); System.out.println(test); //This is secret is printed 

So far so good. However, if I ran it, as soon as I could get the value "2Vhht / L80UlQ184S3rlAWw ==" as my secret, next time it is "MeC4zCf9S5wUUKAu8rvpCQ ==", so, apparently, the key is changing. I assume that I am applying some counter-intuitive logic to the problem and would be grateful if someone could shed some light on: a) what I am doing wrong or b) a solution that allows me to store encrypted password information and is retrieved with the information provided.

My methods are:

 private static final String salt = "SaltySalt"; private static byte [] ivBytes = null; private static byte[] getSaltBytes() throws Exception { return salt.getBytes("UTF-8"); } private static char[] getMasterPassword() { return "SuperSecretPassword".toCharArray(); } private static byte[] getIvBytes() throws Exception { if (ivBytes == null) { //I don't have the parameters, so I'll generate a dummy encryption to create them encrpytString("test"); } return ivBytes; } public static String encrpytString (String input) throws Exception { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256); SecretKey secretKey = factory.generateSecret(spec); SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secret); ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8")); return DatatypeConverter.printBase64Binary(encryptedTextBytes); } public static String decrpytString (String input) throws Exception { byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(input); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256); SecretKey secretKey = factory.generateSecret(spec); SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(getIvBytes())); byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes); return new String(decryptedTextBytes); } 

Thanks for the help!

+5
source share
2 answers

OK, it seems I found the answer to my question. I got information from fooobar.com/questions/141022 / .... From what I understand, IV (initialization vector) is used to add entropy to the encryption process. Each time you create a new cipher, Java creates a slightly different IV. Therefore, there are two solutions:

  • User Fixed IV, or
  • Save IV along with encrypted data.

From what I read, option 1 is not a good practice; therefore, option 2 is. I understand that it should be possible to simply add IV to the encrypted string (since the secret is still required), and therefore IV can be restored when the time comes for decryption.

Here is a complete solution almost . I still get some padding errors when decrypting (see my comment). I donโ€™t have time to spend on it now, as a temporary measure, I immediately try to decrypt the encrypted string and continue trying (iterating) until it works. It seems to have a 50% chance + I'm not encrypting enough to be a performance issue. It would be nice if someone could suggest a fix though (just for the sake of completeness).

 private static final String salt = "SaltySalt"; private static final int IV_LENGTH = 16; private static byte[] getSaltBytes() throws Exception { return salt.getBytes("UTF-8"); } private static char[] getMasterPassword() { return "SuperSecretPassword".toCharArray(); } public static String encrpytString (String input) throws Exception { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256); SecretKey secretKey = factory.generateSecret(spec); SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secret); byte[] ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8")); byte[] finalByteArray = new byte[ivBytes.length + encryptedTextBytes.length]; System.arraycopy(ivBytes, 0, finalByteArray, 0, ivBytes.length); System.arraycopy(encryptedTextBytes, 0, finalByteArray, ivBytes.length, encryptedTextBytes.length); return DatatypeConverter.printBase64Binary(finalByteArray); } public static String decrpytString (String input) throws Exception { if (input.length() <= IV_LENGTH) { throw new Exception("The input string is not long enough to contain the initialisation bytes and data."); } byte[] byteArray = DatatypeConverter.parseBase64Binary(input); byte[] ivBytes = new byte[IV_LENGTH]; System.arraycopy(byteArray, 0, ivBytes, 0, 16); byte[] encryptedTextBytes = new byte[byteArray.length - ivBytes.length]; System.arraycopy(byteArray, IV_LENGTH, encryptedTextBytes, 0, encryptedTextBytes.length); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256); SecretKey secretKey = factory.generateSecret(spec); SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes)); byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes); return new String(decryptedTextBytes); } 
+2
source

Use a static initialization vector, for example. zero IV:

 cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16])); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[16])); 

Since you store passwords, you probably want to use random IV and / or random salt and save them with ciphertext so that the same passwords do not encrypt the same ciphertext.

0
source

All Articles