2

I want to encrypt a password that I need to store in a flat file to be accessed by my java application. I used the accepted answer of this question to model my solution, and it works. Since I know close to nothing about cryptography algorithms, once I read about how weak DES is I wanted to pick another algorithm, and I chose PBEWithHmacSHA256AndAES_256. When I was using PBEWithMD5AndDES,

pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
....
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));

was enough to decrypt the string, but with this algorithm I get this exception:

java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected
    at com.sun.crypto.provider.PBES2Core.engineInit(PBES2Core.java:252)
    at javax.crypto.Cipher.implInit(Cipher.java:806)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
    at javax.crypto.Cipher.init(Cipher.java:1396)
    at javax.crypto.Cipher.init(Cipher.java:1327)

and the only way I could get the decryption to work was if I pass the encryption cipher's algorithm parameters to the decryption one, like this:

pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
AlgorithmParameters ap = pbeCipher.getParameters();
....
pbeCipher.init(Cipher.DECRYPT_MODE, key, ap);

Which does not work for me cause I need to be able to decrypt if the application was restarted in the meantime. So, my question: is this simply how this algorithm is supposed to work, and in that case should I pick a different stronger one (or should I bother at all), or am I doing something wrong?

Community
  • 1
  • 1
Mitio
  • 23
  • 3

2 Answers2

3

An Initialization Vector (IV) is a fixed-size piece of random data needed to initiate the encryption & decryption process. When decrypting, this piece of data needs to be the same as was used when encrypting.

When using PBEWithMD5AndDES the IV is derived from the password, and thus don't need to be specified on the decrypt.

However, with PBEWithHmacSHA256AndAES_256 the IV is random with each encrypt (independently of the password), and thus need to be provided on the decrypt.

You can grap it from the encryption cipher with pbeCipher.getIV();

Usually the IV is pre-pended to the encrypted data. It don't need to be secret.

Ebbe M. Pedersen
  • 7,250
  • 3
  • 27
  • 47
2

The AES cipher needs an extra algorithm parameter - the IV. You can treat it as another SALT (can be random, stored together with the password). The IV for your alg spec should be 16 bytes long

public static String encrypt(String text) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidParameterSpecException, InvalidAlgorithmParameterException {
    Cipher cipher = Cipher.getInstance(ENC_ALG);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALG);

    KeySpec keySpec = new PBEKeySpec(PASSWORD.toCharArray(), SALT, 65536, 256);
    SecretKey key = keyFactory.generateSecret(keySpec);

    IvParameterSpec ivSpec = new IvParameterSpec(IV);    
    PBEParameterSpec paramSpec = new PBEParameterSpec(SALT, 0,ivSpec);

    cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
    byte[] encrypted = cipher.doFinal(text.getBytes());

    return Base64.getEncoder().encodeToString(encrypted);
}

public static String decrypt(String encrypted) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
    Cipher cipher = Cipher.getInstance(ENC_ALG);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALG);

    KeySpec keySpec = new PBEKeySpec(PASSWORD.toCharArray(), SALT, 65536, 256);
    SecretKey key = keyFactory.generateSecret(keySpec);
    IvParameterSpec ivSpec = new IvParameterSpec(IV);    
    PBEParameterSpec paramSpec = new PBEParameterSpec(SALT, 0, ivSpec);
    cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
    byte[] decoded = Base64.getDecoder().decode(encrypted);
    byte[] decrypted = cipher.doFinal(decoded);
    return new String(decrypted);
}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
gusto2
  • 11,210
  • 2
  • 17
  • 36
  • Note, the 3 argument `PBEParameterSpec` is new in Java 8. – Artjom B. Oct 10 '16 at 19:20
  • PBEParameterSpec is since from Java 1.4. However - this is really a sample using Java 8 and not all methods (or constructors) may be present in older versions. But the basic idea is still valid - the AES cipher expects the IV to be specified and I suggest to do that explicitly at the encryption time as well. – gusto2 Oct 11 '16 at 07:55