Android 中 AES 实施可能存在的错误
Possible faults in AES implementation in Android
我正在尝试在 Android 中实施 AES 加密,它使用密码短语生成 SecretKey
。我也一样 byte[]
作为密码的初始化向量,并在使用 PBKDF2.
生成 SecretKey
时作为盐
每次需要 encryption/decryption 时,用户都会提供密码。
截至目前,我只需要在我的数据库中加密一个值(如果这有任何区别的话)。
问题:
- 我想知道使用与 IV 和盐相同的
byte[]
是否会削弱加密?
- 除了 GCM 提供的数据完整性功能之外,是否还有从 CBC 切换到 GCM 的原因?
- 我听说 CBC 容易受到 BEAST 攻击,每条消息使用新的随机 IV,如下所示,可以减轻 BEAST 攻击?
当前源代码:
public class AesEncryption {
private static final int KEY_SIZE = 16;
private static final int OUTPUT_KEY_LENGTH = 256;
private static final int ITERATIONS = 1000;
private String mPassphraseOrPin;
public AesEncryption(String passphraseOrPin) {
mPassphraseOrPin = passphraseOrPin;
}
public void encrypt(String id, String textToEncrypt) throws Exception {
byte[] iv = getIv();
SecretKey secretKey = generateKey(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(textToEncrypt.getBytes("utf-8"));
byte[] ivCipherText = arrayConcat(iv, cipherText);
String encryptedText = Base64.encodeToString(ivCipherText, Base64.NO_WRAP);
storeEncryptedTextInDb(id, encryptedText);
}
public String decrypt(String id) throws Exception {
String encryptedText = getEncryptedTextFromDb(id);
byte[] ivCipherText = Base64.decode(encryptedText, Base64.NO_WRAP);
byte[] iv = Arrays.copyOfRange(ivCipherText, 0, KEY_SIZE);
byte[] cipherText = Arrays.copyOfRange(ivCipherText, KEY_SIZE, ivCipherText.length);
SecretKey secretKey = generateKey(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
String decrypted = new String(cipher.doFinal(cipherText), "utf-8");
return decrypted;
}
public SecretKey generateKey(byte[] salt) throws Exception {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(mPassphraseOrPin.toCharArray(), salt, ITERATIONS, OUTPUT_KEY_LENGTH);
SecretKey tmp = secretKeyFactory.generateSecret(keySpec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
}
private byte[] getIv() {
byte[] salt = new byte[KEY_SIZE];
new SecureRandom().nextBytes(salt);
return salt;
}
private byte[] arrayConcat(byte[] one, byte[] two) {
byte[] combined = new byte[one.length + two.length];
for (int i = 0; i < combined.length; ++i) {
combined[i] = i < one.length ? one[i] : two[i - one.length];
}
return combined;
}
}
I'm wondering if using the same byte[] as IV and salt weakens the encryption?
是的。
对于盐:如果您不随机化盐,那么攻击者可以 pre-calculate table 密码和密码哈希值。这叫做彩虹table。此外,如果任何人有相同的密码,它会产生相同的密钥。强烈建议为每个用户生成一个 salt,并且 - 如果可行 - 每次值为 re-encrypted.
时生成一个新的 salt
对于 IV:如果您 re-encrypt 起始块包含相同的明文,那么密文将重复块。攻击者可以使用它从中提取信息。简单示例:加密 "Yes" 或 "No" 两次与第一次加密 "Yes" 然后 "No" 可以明显区分。通常,您应该生成一个随机 IV 并将其与密文一起存储。如果盐(以及密钥)是随机的,建议 even。如果这在现实世界中有所不同,当然取决于您的威胁模型。
Is there a reason to switch from CBC to GCM other then the data integrity functionallity GCM provides?
GCM 提供明文的完整性和真实性。从功能上讲,它只是 CTR 模式下带有身份验证标签的 AES。如果您需要明文的完整性和真实性(可能还有额外的身份验证数据或 AAD),则取决于您的威胁模型。否则它不会添加任何功能。
如果您只是想对数据保密,那么您可能不需要 GCM。如果您想保护它免受攻击者所做的更改,那么您确实需要它。然而,在那种情况下,您还需要防止重放攻击。
I've read about CBC being prone to BEAST attack, is using a new random IV per message, as demonstrated bellow, mitigates BEAST attack?
BEAST 攻击是针对 SSL/TLS 的基于浏览器的攻击。根据定义,它不适用于数据库加密,尤其是对于静态数据。可能会引发大量攻击,但 BEAST 依赖于 TLS 连接中的动态数据。
备注:
基于长度的攻击经常被遗忘,因为密码/密码模式无法防范它们。它们可能适用 none-the-less。与 CBC 相比,GCM 泄漏了更多有关明文长度的信息。
对于攻击者来说,查看值是否为 re-encrypted 也可能很有趣。
1000 不再被视为安全迭代计数/工作因数。您可能想要对其进行升级(并制定升级策略)。
我正在尝试在 Android 中实施 AES 加密,它使用密码短语生成 SecretKey
。我也一样 byte[]
作为密码的初始化向量,并在使用 PBKDF2.
SecretKey
时作为盐
每次需要 encryption/decryption 时,用户都会提供密码。
截至目前,我只需要在我的数据库中加密一个值(如果这有任何区别的话)。
问题:
- 我想知道使用与 IV 和盐相同的
byte[]
是否会削弱加密? - 除了 GCM 提供的数据完整性功能之外,是否还有从 CBC 切换到 GCM 的原因?
- 我听说 CBC 容易受到 BEAST 攻击,每条消息使用新的随机 IV,如下所示,可以减轻 BEAST 攻击?
当前源代码:
public class AesEncryption {
private static final int KEY_SIZE = 16;
private static final int OUTPUT_KEY_LENGTH = 256;
private static final int ITERATIONS = 1000;
private String mPassphraseOrPin;
public AesEncryption(String passphraseOrPin) {
mPassphraseOrPin = passphraseOrPin;
}
public void encrypt(String id, String textToEncrypt) throws Exception {
byte[] iv = getIv();
SecretKey secretKey = generateKey(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(textToEncrypt.getBytes("utf-8"));
byte[] ivCipherText = arrayConcat(iv, cipherText);
String encryptedText = Base64.encodeToString(ivCipherText, Base64.NO_WRAP);
storeEncryptedTextInDb(id, encryptedText);
}
public String decrypt(String id) throws Exception {
String encryptedText = getEncryptedTextFromDb(id);
byte[] ivCipherText = Base64.decode(encryptedText, Base64.NO_WRAP);
byte[] iv = Arrays.copyOfRange(ivCipherText, 0, KEY_SIZE);
byte[] cipherText = Arrays.copyOfRange(ivCipherText, KEY_SIZE, ivCipherText.length);
SecretKey secretKey = generateKey(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
String decrypted = new String(cipher.doFinal(cipherText), "utf-8");
return decrypted;
}
public SecretKey generateKey(byte[] salt) throws Exception {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(mPassphraseOrPin.toCharArray(), salt, ITERATIONS, OUTPUT_KEY_LENGTH);
SecretKey tmp = secretKeyFactory.generateSecret(keySpec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
}
private byte[] getIv() {
byte[] salt = new byte[KEY_SIZE];
new SecureRandom().nextBytes(salt);
return salt;
}
private byte[] arrayConcat(byte[] one, byte[] two) {
byte[] combined = new byte[one.length + two.length];
for (int i = 0; i < combined.length; ++i) {
combined[i] = i < one.length ? one[i] : two[i - one.length];
}
return combined;
}
}
I'm wondering if using the same byte[] as IV and salt weakens the encryption?
是的。
对于盐:如果您不随机化盐,那么攻击者可以 pre-calculate table 密码和密码哈希值。这叫做彩虹table。此外,如果任何人有相同的密码,它会产生相同的密钥。强烈建议为每个用户生成一个 salt,并且 - 如果可行 - 每次值为 re-encrypted.
时生成一个新的 salt对于 IV:如果您 re-encrypt 起始块包含相同的明文,那么密文将重复块。攻击者可以使用它从中提取信息。简单示例:加密 "Yes" 或 "No" 两次与第一次加密 "Yes" 然后 "No" 可以明显区分。通常,您应该生成一个随机 IV 并将其与密文一起存储。如果盐(以及密钥)是随机的,建议 even。如果这在现实世界中有所不同,当然取决于您的威胁模型。
Is there a reason to switch from CBC to GCM other then the data integrity functionallity GCM provides?
GCM 提供明文的完整性和真实性。从功能上讲,它只是 CTR 模式下带有身份验证标签的 AES。如果您需要明文的完整性和真实性(可能还有额外的身份验证数据或 AAD),则取决于您的威胁模型。否则它不会添加任何功能。
如果您只是想对数据保密,那么您可能不需要 GCM。如果您想保护它免受攻击者所做的更改,那么您确实需要它。然而,在那种情况下,您还需要防止重放攻击。
I've read about CBC being prone to BEAST attack, is using a new random IV per message, as demonstrated bellow, mitigates BEAST attack?
BEAST 攻击是针对 SSL/TLS 的基于浏览器的攻击。根据定义,它不适用于数据库加密,尤其是对于静态数据。可能会引发大量攻击,但 BEAST 依赖于 TLS 连接中的动态数据。
备注:
基于长度的攻击经常被遗忘,因为密码/密码模式无法防范它们。它们可能适用 none-the-less。与 CBC 相比,GCM 泄漏了更多有关明文长度的信息。
对于攻击者来说,查看值是否为 re-encrypted 也可能很有趣。
1000 不再被视为安全迭代计数/工作因数。您可能想要对其进行升级(并制定升级策略)。