Android N InvalidKeyException

时间:2016-08-25 18:22:15

标签: java android android-7.0-nougat javax.crypto

我的应用程序仍然以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中传递的密钥不是null。 为什么这不适用于运行7.0的手机?

[来自评论的编辑]

上面的代码使用:

 AES_ALGO = "PBEWITHSHA256AND128BITAES-CBC-BC";

1 个答案:

答案 0 :(得分:0)

您没有向我们展示的唯一内容是AES_ALGO的值,这可能表示PBE加密,密钥不是使用任何类型的PBE密钥派生(例如PBKDF1或PBKDF2)生成的。这些键将返回与"AES"不同类型的键算法。

显然,在这方面各种版本的Android之间存在差异。这可能不是第一次在Android提供商中存在使用差异,因为他们会定期更改实施。 API仍然相同,但算法的实现有所不同。

要使用CBC模式加密进行加密 - 如PBE加密方法所使用 - 请查看使用"AES/CBC/PKCS5Padding"的无数示例。不要忘记你需要正确处理IV值,这是Bouncy Castle PBE密码本身所包含的内容。

[编辑]

令我惊讶的是,BC“计算”的PBE密钥只包含PBE规范的密码和迭代计数。只有与密码一起使用的util是计算的值。密钥的类型不只是SecretKey,而是:

  

org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey

它还包含例如占位符IV值。因此,计算目前根本不起作用也就不足为奇了。更令人惊讶的是它似乎以前有效。

故事的道德,不要肆无忌惮地混淆Ciphers和SecretKeys。它可能适用于特定版本,但是当开发人员决定执行更严格的检查时,代码可能会废弃。

我尝试简单地使用PBE算法的名称作为密钥,但显然它永远不会起作用。