1

Please excuse the hackjob! Still new at coding and was utilizing a lot of sys outs to troubleshoot, and this is my first post at StackOverflow. Thank you for the help!

So I've been working on trying to encrypt String objects using the javax.crypto.Cipher API, and I have found some success, but only when it is using the same instance of the Cipher object. However, for the purposes of my project, I am encrypting text (String) to and decrypting text from a text file, and will not be accessing the same Cipher object every time.

I believe the issue is not from converting between the byte arrays and Strings, since the Base64 encoder seems to have taken care of that problem. The output of the byte arrays are identical pre-encoding and post-decoding, so that should isolate it as an issue during the decryption phase. What can be done so that my decryptPW method can use a different Cipher instance (with the same arguments passed) and not trigger a BadPaddingException?

private static String encryptPW(String pw){
    byte[] pwBytes = pw.getBytes();
    byte[] keyBytes = "0123456789abcdef".getBytes();
    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    try {
        Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ciph.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] encryptedBytes = ciph.doFinal(pwBytes);
        pw = Base64.getEncoder().encodeToString(ciph.doFinal(pwBytes));

        for (byte b : encryptedBytes){
            System.out.print(b);
        }
        System.out.println();
    } catch (Exception e){
        e.printStackTrace();
    }
    return pw;
}

private static String decryptPW(String pw){
    byte[] pwBytes = Base64.getDecoder().decode(pw.getBytes());
    for (byte b : pwBytes){
        System.out.print(b);
    }
    System.out.println();

    byte[] keyBytes = "0123456789abcdef".getBytes();
    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    try {
        Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ciph.init(Cipher.DECRYPT_MODE, keySpec, ciph.getParameters());
        pw = new String(ciph.doFinal(pwBytes));    
    } catch (Exception e){
        e.printStackTrace();
    }
    return pw;
}

Again, thank you!

Tommy
  • 23
  • 6
  • Why are you initialize g the two Cipher objects differently? And I don't just retr to the cipher mode. – user207421 Jul 26 '17 at 01:26
  • Hi EJP, for the decryptPW method, I am calling the Cipher.init method with different arguments because if I don't put the 3rd argument, it raises this exception: java.security.InvalidKeyException: Parameters missing – Tommy Jul 26 '17 at 01:31
  • So why not put that argument both times? – user207421 Jul 26 '17 at 01:34
  • Just tried it out. Gives same exception: javax.crypto.BadPaddingException: Given final block not properly padded – Tommy Jul 26 '17 at 01:37
  • **Don't ever encrypt passwords, hash them**. Your site/application will end up on one of those name and shame sites and everyone will down vote and rate it badly. – Luke Joshua Park Jul 26 '17 at 06:15

1 Answers1

1

As you use CBC mode, you need to save the random Initialization Vector (IV) from the encryption cipher and provide it to the decryption cipher.

You can get it from the encryption cipher after init like:

ciph.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] iv = ciph.getIV();

And provide it to the decrypt cipher with something like:

ciph.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));

You need to come up with a method of saving the IV. A common way is to prefix the encrypted data with the IV, so it's available when you need to initialize your decrypt cipher. It don't need to be secret.

Ebbe M. Pedersen
  • 7,250
  • 3
  • 27
  • 47
  • Thank you so much! This is the exact solution I was looking for! I will modify the rest of my code with your suggestion to store the IV with the encrypted password. – Tommy Jul 26 '17 at 03:14