我使用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的所有好处吗?
因为在尝试使用错误的密钥时抛出的异常是不同的。使用BadPaddingException
时,如果使用IvParameterSpec
,则会在API 21 + AEADBADTagException
上抛出API GCMParameterSpec
。
仅使用IvParameterSpec
所有Android API级别并通过BadPaddingException
验证完整性是否正确且安全?我不想为不同的平台实现不同的实现,所以我只想使用一个。
此外,在API 21+上,如果我使用GCMParameterSpec
进行加密,然后使用IvParameterSpec
进行解密,则解密就可以了!反之亦然。这有什么用?
如果在API 19上无法实现上述目标,那么我可以选择使用哪种加密算法和策略(AES/CBC/PKCS5Padding
与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);
}
}
}
此外,请随意建议改进所提供的课程以及您的答案,谢谢。
答案 0 :(得分:3)
我还想知道何时尝试解密并使用错误的密钥。
没关系,但请理解,无效标签可能意味着标签本身已被更改,密文被更改,IV被更改,AAD被更改或确实密钥不正确。
您还可以使用密钥检查值或类似的东西来检查密钥大小是否正确,然后再进行解密。但请注意,对手也可以改变该检查值。
那么通过更改这些参数会发生什么?我仍然可以获得GCM的所有好处吗?
可以肯定的是,GCM的改造方式基本上是兼容的,但仍然有更多的配置选项(主要是标签大小) - 如果你需要配置它。 AEADBADTagException
是BadPaddingException
,因此代码应该适用于每个代码,即使AEADBADTagException
更具体。
仅使用
IvParameterSpec
所有Android API级别并通过BadPaddingException
验证完整性是否正确且安全?我不想为不同的平台实现不同的实现,所以我只想使用一个。
当然可以。请注意,只有标记可以抛出BadPaddingException
,因此这样的异常可以正确识别身份验证问题。
此外,在API 21+上,如果我使用GCMParameterSpec加密,然后使用IvParameterSpec解密它解密就好了!反之亦然。这有什么用?
您的代码针对每种类型的参数规范运行,因为您指定的标记大小与默认值相同:128位。它不适用于较小的标签尺寸。
代码评论:
charset
应为常量(static final
); SecretKey
实例传递; ArrayIndexOutOfBounds
异常);