我有一个应用程序,仍然是针对Android 6.0,但我得到加密错误时,试图安装在Android N(我已经尝试瞄准N,太)。下面是堆栈跟踪:
W/System.err: java.security.InvalidKeyException: Algorithm requires a PBE key
W/System.err: at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:564)
W/System.err: at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:1006)
W/System.err: at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2977)
W/System.err: at javax.crypto.Cipher.tryCombinations(Cipher.java:2884)
W/System.err: at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2789)
W/System.err: at javax.crypto.Cipher.chooseProvider(Cipher.java:956)
W/System.err: at javax.crypto.Cipher.init(Cipher.java:1199)
W/System.err: at javax.crypto.Cipher.init(Cipher.java:1143)
如您所见,它在调用Cipher.init时发生。下面是我的aesDecrypt方法:
public static String aesDecrypt(String data, String password) {
try {
String aesKey = getAesKey(password);
byte[] keyValue = Base64.decode(aesKey, Base64.NO_WRAP);
SecretKey key = new SecretKeySpec(keyValue, "AES");
Cipher c = Cipher.getInstance(AES_ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] dataB = Base64.decode(data, Base64.NO_WRAP);
byte[] decVal = c.doFinal(dataB);
return new String(decVal);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
和我的getAesKey:
private static String getAesKey(String password) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(password.getBytes("UTF-8"));
return Base64.encodeToString(hash, Base64.NO_WRAP);
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
我已经验证了我在c.init中传递的键不是空的。为什么这不能在运行7.0的手机上工作?
[EDIT from comments]
上面的代码使用: AES_ALGO = "PBEWITHSHA256AND128BITAES-CBC-BC";
您唯一没有向我们展示的是AES_ALGO
的值,这可能表示PBE加密,该密钥不是使用任何类型的PBE密钥派生(例如PBKDF1或PBKDF2)生成的。这些密钥将返回不同于"AES"
的密钥算法。
显然在这方面不同版本的Android存在差异。这并不是Android供应商之间第一次出现使用差异,因为他们经常改变实现方式。API仍然相同,但算法的实现略有不同。
要使用CBC模式加密(如PBE加密方法所使用)进行加密,请查看使用"AES/CBC/PKCS5Padding"
的无数示例。不要忘记您需要正确处理IV值,这是包含在Bouncy Castle PBE密码本身中的内容。
[编辑]
令我惊讶的是,BC"计算"的PBE密钥仅包含PBE规范的密码和迭代计数。只有在与密码一起使用时才计算出值。键的类型不仅仅是SecretKey
,还有:
org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey
它还包含占位符,例如IV值。因此,这种计算目前根本行不通也就不足为奇了。更令人惊讶的是,它以前似乎奏效过。
这个故事的寓意是,无论如何都不要把密码和秘钥混为一谈。它可能适用于特定的版本,但是当开发人员决定执行更严格的检查时,代码可能会崩溃。
我尝试简单地使用PBE算法的名称作为密钥,但显然这永远无法工作。