【发布时间】:2018-09-20 04:31:26
【问题描述】:
我正在使用AES/GCM/NoPadding 算法加密 Android 上的一些数据(API 19 及更高版本),然后再将其解密。
我使用的密钥大小是 32 字节,是提供给我的
除了加密之外,我还想知道我何时尝试解密并使用了错误的密钥。这就是为什么我更喜欢使用 GCM 作为我的模式来获得验证完整性的好处(我相信可以安全地假设密文或密钥有问题是否会导致错误的解密异常而不是乱码)
我面临的问题是,在 Android API 19 上,使用上述算法并使用 GCMParameterSpec 初始化密码,我得到一个 NoSuchAlgorithmException,我自己没有指定任何提供程序,允许 Android 为我选择一个可以支持我的算法。在 21+ 上,算法可用。
这就是我的初始化方式(解密类似),整个类都贴在这篇文章的末尾。
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv));
但是,如果我使用IvParameterSpec(iv) 作为我的AlgorithmParameters 而不是GCMParameterSpec,那么代码可以正常工作。
那么通过更改这些参数会发生什么?我还能获得 GCM 的所有相同好处吗?
因为尝试使用错误的键时抛出的异常是不同的。在 API 19 上,BadPaddingException 在使用 IvParameterSpec 时抛出,在 API 21+ 上,AEADBADTagException 在使用 GCMParameterSpec 时抛出。
在所有 Android API 级别中仅使用 IvParameterSpec 并通过 BadPaddingException 验证完整性是否正确且安全?我不想针对不同的平台有不同的实现,所以我只想使用一个。
另外,在 API 21+ 上,如果我使用 GCMParameterSpec 加密,然后使用 IvParameterSpec 解密它就可以解密!反之亦然。效果如何?
如果在 API 19 上无法实现上述操作,那么我有哪些可能的选项可用作加密算法和使用策略(AES/CBC/PKCS5Padding with HMAC?)来验证密钥的完整性。
全类代码:
import android.util.Base64;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
final class Encryption {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int TAG_LENGTH_BIT = 128;
private static final int IV_LENGTH_BYTE = 12;
private final SecureRandom secureRandom;
private Cipher cipher;
private final Charset charset = StandardCharsets.UTF_8;
public Encryption() {
secureRandom = new SecureRandom();
}
public String encrypt(byte[] key, String rawData) throws Exception {
try {
byte[] iv = new byte[IV_LENGTH_BYTE];
secureRandom.nextBytes(iv);
cipher = Cipher.getInstance(ALGORITHM);
//This is where I switch to IvParameterSpec(iv)
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv));
byte[] encrypted = cipher.doFinal(rawData.getBytes(charset));
ByteBuffer byteBuffer = ByteBuffer.allocate(1 + iv.length + encrypted.length);
byteBuffer.put((byte) iv.length);
byteBuffer.put(iv);
byteBuffer.put(encrypted);
return Base64.encodeToString(byteBuffer.array(), Base64.NO_WRAP);
} catch (Exception e) { //ignore this SO
throw new Exception(e);
}
}
public String decrypt(byte[] key, String encryptedData) throws Exception {
try {
ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData, Base64.NO_WRAP));
int ivLength = byteBuffer.get();
byte[] iv = new byte[ivLength];
byteBuffer.get(iv);
byte[] encrypted = new byte[byteBuffer.remaining()];
byteBuffer.get(encrypted);
cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv));
byte[] decrypted = cipher.doFinal(encrypted);
//Paranoia
Arrays.fill(iv, (byte) 0);
Arrays.fill(rawEncryptionKey, (byte) 0);
Arrays.fill(encrypted, (byte) 0);
return new String(decrypted, charset);
} catch (Exception e) { //ignore this SO
// On API 19 BadPaddingException is thrown when IvParameterSpec is used
// On API 21+ AEADBADTagException is thrown
throw new Exception("could not decrypt", e);
}
}
}
另外,请随时提出改进所提供课程的建议以及您的答案,谢谢。
【问题讨论】:
标签: java android encryption cryptography aes-gcm