首页 > 解决方案 > 密码算法 AES CBC 不安全的 Android

问题描述

我目前在我的 Android 应用程序上使用 Cipher cipher = Cipher.getInstance ("AES / CBC / PKCS5Padding")。负责应用程序安全的人告诉我:“还应该注意,AES 加密在不是 AES CBC 时是安全的。”,所以他们希望我修改算法。

我尝试了 AES/GCM/NoPadding 和 AES/CTR/PKCS5Padding,如果我卸载旧版本并重新安装,该应用程序可以工作,但如果我将它安装在带有 CBC 的版本之上,我会收到以下错误:javax.crypto .BadPaddingException:垫块损坏

我该如何解决这个问题?恐怕如果我更新商店中的应用程序,最终用户会发生此错误。

笔记:

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...

更新!

我决定让代码更简洁,在这里你可以理解。我的目标是将“AES / CBC / PKCS7Padding”替换为“AES / GCM / NoPadding”,因为他们认为 AES 与 CBC 不安全 :( 此 GCM 实现出现的问题:我必须在密码上使用 GCMParameterSpec 吗?需要导入 BouncyCastle 提供程序?

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);
}

标签: javaandroidencryptionaes

解决方案


推荐阅读