0

I am currently using Cipher cipher = Cipher.getInstance ("AES / CBC / PKCS5Padding") on my Android app. Those responsible for the security of the app tell me this: "It should also be noted that AES encryption is secure when it is not AES CBC.", so they want me to modify the algorithm.

I tried AES/GCM/NoPadding and AES/CTR/PKCS5Padding, if I uninstall the old version and install it again, the app works, but if I install it on top of a version with CBC I get the following error: javax.crypto.BadPaddingException: pad block corrupted

How can I solve this? I am afraid that if I update the app that is in the store, this error will happen to end users.

Notes:

public static String encrypt (String plaintext, String pwd) {
        try {
            byte [] salt = generateSalt ();
            SecretKey key = deriveKey (salt, pwd);
            Cipher cipher = Cipher.getInstance (CIPHER_ALGORITHM);

            byte [] iv = generateIv (cipher.getBlockSize ());
            IvParameterSpec ivParams = new IvParameterSpec (iv);
            cipher.init (Cipher.ENCRYPT_MODE, key, ivParams);
            byte [] cipherText = cipher.doFinal (plaintext.getBytes ("UTF-8"));

            if (salt! = null) {
                return String.format ("% s% s% s% s% s", toBase64 (salt), DELIMITER,
                        toBase64 (iv), DELIMITER, toBase64 (cipherText));
            }

            return String.format ("% s% s% s", toBase64 (iv), DELIMITER,
                    toBase64 (cipherText));
        } catch (GeneralSecurityException e) {
            throw new RuntimeException (e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException (e);
        }
    }


public static String decrypt (byte [] cipherBytes, SecretKey key, byte [] iv) {
        try {
            Cipher cipher = Cipher.getInstance (CIPHER_ALGORITHM);
            IvParameterSpec ivParams = new IvParameterSpec (iv);
            cipher.init (Cipher.DECRYPT_MODE, key, ivParams);
            byte [] plaintext = cipher.doFinal (cipherBytes);
            String plainrStr = new String (plaintext, "UTF-8");

            return plainrStr;
        } catch (GeneralSecurityException e) {
            throw new RuntimeException (e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException (e);
        }
    }

etc...

Update!

I decided to make the code cleaner and here it is for you to understand. My goal was to replace "AES / CBC / PKCS7Padding" with "AES / GCM / NoPadding", as they consider AES with CBC unsafe :( Questions that arose for this GCM implementation: do I have to use GCMParameterSpec on the cipher? Do I need to import the BouncyCastle provider?

public static final String PBKDF2_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
//private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";

private static String DELIMITER = "]";
private static final int PKCS5_SALT_LENGTH = 8;
private static SecureRandom random = new SecureRandom();
private static final int ITERATION_COUNT = 1000;
private static final int KEY_LENGTH = 256;


 public static String encrypt(String plaintext, String pwd) {
    byte[] salt = generateSalt();

    SecretKey key = deriveKey(pwd, salt);

    try {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        byte[] iv = generateIv(cipher.getBlockSize());
        IvParameterSpec ivParams = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
        byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8"));

        return String.format("%s%s%s%s%s", toBase64(salt), DELIMITER, toBase64(iv), DELIMITER, toBase64(cipherText));

    } catch (GeneralSecurityException | UnsupportedEncodingException e) {
        throw new RuntimeException(e);
    }
}

public static String decrypt(String ciphertext, String pwd) {
    String[] fields = ciphertext.split(DELIMITER);
    if (fields.length != 3) {
        throw new IllegalArgumentException("Invalid encypted text format");
    }
    byte[] salt = fromBase64(fields[0]);
    byte[] iv = fromBase64(fields[1]);
    byte[] cipherBytes = fromBase64(fields[2]);
    SecretKey key = deriveKey(pwd, salt);

    try {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        IvParameterSpec ivParams = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
        byte[] plaintext = cipher.doFinal(cipherBytes);
        return new String(plaintext, "UTF-8");
    } catch (GeneralSecurityException e) {
        throw new RuntimeException(e);
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
    }
}

private static byte[] generateSalt() {
    byte[] b = new byte[PKCS5_SALT_LENGTH];
    random.nextBytes(b);
    return b;
}

private static byte[] generateIv(int length) {
    byte[] b = new byte[length];
    random.nextBytes(b);
    return b;
}

private static SecretKey deriveKey(String pwd, byte[] salt) {

    try {
        KeySpec keySpec = new PBEKeySpec(pwd.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_DERIVATION_ALGORITHM);
        byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
        return new SecretKeySpec(keyBytes, "AES");
    } catch (GeneralSecurityException e) {
        throw new RuntimeException(e);
    }
}

private static String toBase64(byte[] bytes) {
    return Base64.encodeToString(bytes, Base64.NO_WRAP);
}

public static byte[] fromBase64(String base64) {
    return Base64.decode(base64, Base64.NO_WRAP);
}
ElHashashin
  • 25
  • 1
  • 7
  • What do you mean with "on top"? Usually it would be used **instead** of the old algorithm. Btw: it is the "CBC" algorithm. – Michael Fehr Feb 19 '21 at 13:55
  • @MichaelFehr ty for the typo, it is CBC. What I was trying to say was that I run my app on Android Studio (with the new implementation) on my emulator. The old version of the app (CBC) is on that emulator. Then the error occurs, pad block corrupted. – ElHashashin Feb 19 '21 at 14:02
  • It is possible that something you have encrypted with this padding "PKCS5Padding", but while decrypting you are using "NoPadding" or vice versa, keep the padding same for both encryption & decryption, you can check this answer - https://stackoverflow.com/a/15158609/5353830 – Rai_Gaurav Feb 19 '21 at 14:03
  • @Rai_Gaurav ty for the comment. My padding is the same, PKCS5Padding. What mode should I use to support AES and PKCS5Padding? Since CBS is considered insecure :( here [link](https://developer.android.com/reference/javax/crypto/Cipher) we can find CBC CFB CTR CTS ECB OFB for AES – ElHashashin Feb 19 '21 at 14:15
  • @ElHashashin can you pin point exactly when this error comes, is it while encryption or decryption? Also will suggest you to look into how you derive your key both the times while encryption & decryption, you should look into your deriveKey (salt, pwd) method – Rai_Gaurav Feb 19 '21 at 14:37
  • 1
    _It should also be noted that AES encryption is secure when it is not AES CBC_ is a bit vague. It possibly addresses the lack of authentication with CBC, but that also applies to CTR, CFB, etc. Authentication can be added with an MAC. One mode with buitin authentication is GCM. Apart from that, it doesn't seem to be a real encryption problem, since the GCM implementation works when the CBC version has been uninstalled before. It is rather an installation problem, similar to e.g. https://stackoverflow.com/questions/22792875/android-application-update-vs-remove-and-install. – Topaco Feb 19 '21 at 14:44
  • @Rai_Gaurav I updated the content of the question so you can better understand the current code, thanks for your help – ElHashashin Feb 19 '21 at 18:20
  • @Topaco yes, I already learn that... but when I try to implement the GCM it shows the following error: AES-GCM: AEADBadTagException: mac check in GCM failed... I've searched the Google for solutions, but they all required BouncyCastle I guess... – ElHashashin Feb 19 '21 at 18:23
  • The code works for AES/GCM/NoPadding on my machine, please post an MCVE. _do I have to use GCMParameterSpec on the cipher?_ You should, but with Android also `IvParameterSpec` works. Btw, the recommended IV length for GCM is 12 bytes. _Do I need to import the BouncyCastle provider?_ Not necessary with your implementation. – Topaco Feb 20 '21 at 15:19

0 Answers0