java - 密码算法 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);
}
解决方案
推荐阅读
- bash - 在 bash 终端中循环并应用命令
- c# - NamedPipe WriteFile 被阻止(C++ 服务器 C# 客户端)
- kotlin - 如何使用 bit 在 Kotlin 中存储配置?
- sql - 使用 SQL 查询从具有不同日期的同一表中的先前价格中减去当前价格
- spring-boot - Spring Boot 中的哪个实体类应该具有级联功能?
- npm - semver + latest 具有 NPM 依赖项
- azure - 在参数文件中动态添加 API 连接
- javascript - react-fullpage 中的 fullpage_api.getActiveSection()
- codeigniter - codeigniter 中出现意外的“foreach”
- amazon-web-services - 扩展 AWS Aurora RDS 实例需要多长时间