我正在关注Google提供的ConfirmCredential Android示例,但它只展示了如何加密数据。当我尝试解密时,我得到例外:
java.security.InvalidKeyException: IV required when decrypting. Use IvParameterSpec or AlgorithmParameters to provide it.
我使用以下代码:
String transforation = KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7;
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
// encrypt
Cipher cipher = Cipher.getInstance(transforation);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String encriptedPassword = cipher.doFinal("Some Password".getBytes("UTF-8"));
// decrypt
cipher = Cipher.getInstance(transforation);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
String password = new String(cipher.doFinal(encriptedPassword), "UTF-8"));
该行引发异常:
cipher.init(Cipher.DECRYPT_MODE, secretKey);
知道在这种情况下解密的正确方法是什么?
答案 0 :(得分:14)
基本上你必须为解密模式传递IvParameterSpec,而你不应该手动将其设置为加密模式。
所以这就是我所做的,而且效果很好:
private initCipher(int mode) {
try {
byte[] iv;
mCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
IvParameterSpec ivParams;
if(mode == Cipher.ENCRYPT_MODE) {
mCipher.init(mode, generateKey());
ivParams = mCipher.getParameters().getParameterSpec(IvParameterSpec.class);
iv = ivParams.getIV();
fos = getContext().openFileOutput(IV_FILE, Context.MODE_PRIVATE);
fos.write(iv);
fos.close();
}
else {
key = (SecretKey)keyStore.getKey(KEY_NAME, null);
File file = new File(getContext().getFilesDir()+"/"+IV_FILE);
int fileSize = (int)file.length();
iv = new byte[fileSize];
FileInputStream fis = getContext().openFileInput(IV_FILE);
fis.read(iv, 0, fileSize);
fis.close();
ivParams = new IvParameterSpec(iv);
mCipher.init(mode, key, ivParams);
}
mCryptoObject = new FingerprintManager.CryptoObject(mCipher);
} catch(....)
}
您还可以使用Base64对IV数据进行编码,并将其保存在共享首选项下,而不是将其保存在文件下。
在任何一种情况下,如果您已为Android M使用新的完整备份功能,请记住,此数据应 NOT 允许备份/恢复,因为当用户重新安装时,该数据无效在其他设备上应用或安装应用。
答案 1 :(得分:3)
我在github项目中为google提供的示例创建了一个问题(链接到问题here)。我得到的回应是我必须使用加密值时生成的IV。 (与@Qianqian提供的解决方案相同)
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(encryptCipher.getIV()));
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
我创建了一个示例应用程序,展示了如何执行此操作。它可以在github here上找到。
希望这对某人有用。
答案 2 :(得分:0)
这个怎么样,
<p>
<strong>Name:</strong>
<%= @trip.name %>
</p>
<p>
<strong>Location:</strong>
<%= @trip.location %>
</p>
<p>
<strong>Start Date:</strong>
<%= @trip.start %>
</p>
<p>
<strong>End Date:</strong>
<%= @trip.end %>
</p>
如果您使用来自Google repo的示例代码,上面的行也可以使用,对于每个mChiper.init,我们需要使用指纹进行一次身份验证,这意味着需要两个指纹身份验证,一个用于加密,一个用于解密。