Android N InvalidKeyException
Android N InvalidKeyException
我有一个应用程序仍然以 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 的密钥不为空。
为什么这在 phone 运行 7.0 上不起作用?
[根据评论编辑]
以上代码使用:
AES_ALGO = "PBEWITHSHA256AND128BITAES-CBC-BC";
您唯一没有向我们展示的是 AES_ALGO
的值,这可能表明 PBE 加密,其密钥不是使用任何类型的 PBE 密钥推导(例如 PBKDF1 或PBKDF2).这些密钥将 return 与 "AES"
.
不同类型的密钥算法
显然 Android 的各个版本在这方面存在差异。这不是第一次 Android 提供商内部出现使用差异,因为他们经常更改实现。 API 仍然相同,但算法的实现有所不同。
要使用 CBC 模式加密进行加密 - 正如 PBE 加密方法所使用的那样 - 请查看无数使用 "AES/CBC/PKCS5Padding"
的示例。不要忘记您需要正确处理 IV 值,这是包含在 Bouncy Castle PBE 密码本身中的东西。
[编辑]
令我惊讶的是,BC "calculated" 的 PBE 密钥仅包含 PBE 规范的密码和迭代计数。只有与密码一起使用的 util 才是计算的值。密钥的类型不仅仅是 SecretKey
而是:
org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey
它还包含占位符,例如IV 值。因此,计算目前根本不起作用也就不足为奇了。更令人惊讶的是,它以前似乎有效。
这个故事的寓意是,不要随便混用密码和密钥。它可能适用于特定版本,但当开发人员决定执行更严格的检查时,代码可能会出错。
我试过简单地使用 PBE 算法的名称作为密钥,但显然这永远行不通。
我有一个应用程序仍然以 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 的密钥不为空。 为什么这在 phone 运行 7.0 上不起作用?
[根据评论编辑]
以上代码使用:
AES_ALGO = "PBEWITHSHA256AND128BITAES-CBC-BC";
您唯一没有向我们展示的是 AES_ALGO
的值,这可能表明 PBE 加密,其密钥不是使用任何类型的 PBE 密钥推导(例如 PBKDF1 或PBKDF2).这些密钥将 return 与 "AES"
.
显然 Android 的各个版本在这方面存在差异。这不是第一次 Android 提供商内部出现使用差异,因为他们经常更改实现。 API 仍然相同,但算法的实现有所不同。
要使用 CBC 模式加密进行加密 - 正如 PBE 加密方法所使用的那样 - 请查看无数使用 "AES/CBC/PKCS5Padding"
的示例。不要忘记您需要正确处理 IV 值,这是包含在 Bouncy Castle PBE 密码本身中的东西。
[编辑]
令我惊讶的是,BC "calculated" 的 PBE 密钥仅包含 PBE 规范的密码和迭代计数。只有与密码一起使用的 util 才是计算的值。密钥的类型不仅仅是 SecretKey
而是:
org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey
它还包含占位符,例如IV 值。因此,计算目前根本不起作用也就不足为奇了。更令人惊讶的是,它以前似乎有效。
这个故事的寓意是,不要随便混用密码和密钥。它可能适用于特定版本,但当开发人员决定执行更严格的检查时,代码可能会出错。
我试过简单地使用 PBE 算法的名称作为密钥,但显然这永远行不通。