Android 4.2 破解了我的加密/解密代码并且提供的解决方案不起作用 [英] Android 4.2 broke my encrypt/decrypt code and the provided solutions don't work

查看:13
本文介绍了Android 4.2 破解了我的加密/解密代码并且提供的解决方案不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我已经看到了Android 4.2 破解了我的 AES 加密/解密代码Android 4.2 上的加密错误以及提供的解决方案:

First of all, I've already seen Android 4.2 broke my AES encrypt/decrypt code and Encryption error on Android 4.2 and the provided solution:

SecureRandom sr = null;
if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) {
    sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
} else {
    sr = SecureRandom.getInstance("SHA1PRNG");
}

对我不起作用,因为在 Android 4.2 中解码在 Android<4.2 中加密的数据时,我得到:

doesn't work for me, because, when decoding data encrypted in Android<4.2 in Android 4.2, I get:

javax.crypto.BadPaddingException: pad block corrupted
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:709)

我的代码很简单,一直运行到 Android 4.2:

My code is quite simple, and was working until Android 4.2:

public static byte[] encrypt(byte[] data, String seed) throws Exception {

    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
    secrand.setSeed(seed.getBytes());
    keygen.init(128, secrand);

    SecretKey seckey = keygen.generateKey();
    byte[] rawKey = seckey.getEncoded();

    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    return cipher.doFinal(data);
}

public static byte[] decrypt(byte[] data, String seed) throws Exception {

    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
    secrand.setSeed(seed.getBytes());
    keygen.init(128, secrand);

    SecretKey seckey = keygen.generateKey();
    byte[] rawKey = seckey.getEncoded();

    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    return cipher.doFinal(data);
}

我的猜测是,默认提供程序并不是 Android 4.2 中唯一改变的东西,否则我的代码将适用于建议的解决方案.

My guess is that the default provider wasn't the only thing that changed in Android 4.2, otherwise my code would work with the proposed solution.

我的代码基于很久以前在 StackOverflow 上找到的一些帖子;我发现它与提到的帖子不同,因为它只是加密和解密字节数组,而其他解决方案加密和解密字符串(我认为是 HEX 字符串).

My code was based on some post I found here at StackOverflow a long time ago; I see that it differs from the mentioned posts as it just crypts and decrypts byte arrays, whereas the others solutions crypt and decrypt Strings (HEX Strings, I think).

这和种子有关吗?它是否有最小/最大长度、字符限制等?

Does it have to do with the seed? Does it have a min/max length, restriction of chars, etc?

有什么想法/解决方案吗?

Any idea / solution?

编辑:经过大量测试,我发现有2个问题:

EDIT: After a lot of tests, I see that there are 2 problems:

  1. 在 Android 4.2 (API 17) 中更改了提供程序 -> 这个很容易修复,只需应用我在帖子顶部提到的解决方案

  1. The provider changed in Android 4.2 (API 17) -> This one is easy to fix, just apply the solution I mentioned at top of the post

BouncyCastle 在 Android 2.2 (API 8)->Android2.3 (API 9) 中从 1.34 变成了 1.45,所以我之前讲的解密问题和这里描述的一样:BouncyCastle AES 升级到 1.45 时出错

BouncyCastle changed from 1.34 to 1.45 in Android 2.2 (API 8)->Android2.3 (API 9), so the decryption problem I previously told is the same as described here: BouncyCastle AES error when upgrading to 1.45

所以现在的问题是:有没有办法在 BouncyCastle 1.45+ 中恢复在 BouncyCastle 1.34 中加密的数据?

推荐答案

首先声明:

切勿使用 SecureRandom 来派生密钥!这是坏的,没有意义!

DO NOT ever use SecureRandom to derive a key! This is broken and doesn't make sense!

问题中的以下代码块尝试从密码中确定性地派生密钥,称为种子",因为密码用于种子"随机数生成器.

The following block of code from the question tries to deterministically derive a key from a password, called the "seed" as the password is used to "seed" the random number generator.

KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();

但是,"SHA1PRNG" 算法的定义不明确,"SHA1PRNG" 的实现可能会返回不同的甚至完全随机的密钥结果.

However, the "SHA1PRNG" algorithm is not well defined and implementations of "SHA1PRNG" may return different or even fully random keys as a result.

如果您从磁盘读取 AES 密钥,只需存储实际密钥,不要经历这种奇怪的舞蹈.您可以通过执行以下操作从字节中获取用于 AES 使用的 SecretKey:

If you're reading an AES key from disk, just store the actual key and don't go through this weird dance. You can get a SecretKey for AES usage from the bytes by doing:

    SecretKey key = new SecretKeySpec(keyBytes, "AES");

如果您使用密码来派生密钥,请遵循 Nelenkov 的优秀教程 需要注意的是,一个好的经验法则是盐的大小应该与键输出的大小相同.

If you're using a password to derive a key, follow Nelenkov's excellent tutorial with the caveat that a good rule of thumb is the salt size should be the same size as the key output.

iterationCount(工作系数)当然会发生变化,并且应该随着 CPU 功率的提高而变化 - 通常建议不要低于 2018 年的 40 到 100K.请注意 PBKDF2只会增加猜测密码的持续时间延迟;它不能替代非常弱的密码.

The iterationCount (work factor) is of course subject to change and should be changed as CPU power progresses - generally it is recommended not to go lower than 40 to 100K as of 2018. Beware that PBKDF2 only adds a constant time delay to guessing passwords; it is not a replacement for really weak passwords.

看起来像这样:

    /* User types in their password: */
    String password = "password";

    /* Store these things on disk used to derive key later: */
    int iterationCount = 1000;
    int saltLength = 32; // bytes; should be the same size as the output (256 / 8 = 32)
    int keyLength = 256; // 256-bits for AES-256, 128-bits for AES-128, etc
    byte[] salt; // Should be of saltLength

    /* When first creating the key, obtain a salt with this: */
    SecureRandom random = new SecureRandom();
    byte[] salt = new byte[saltLength];
    random.nextBytes(salt);

    /* Use this to derive the key from the password: */
    KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,
                iterationCount, keyLength);
    SecretKeyFactory keyFactory = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
    byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
    SecretKey key = new SecretKeySpec(keyBytes, "AES");

就是这样.任何其他你不应该使用的东西.

That's it. Anything else you should not use.

这篇关于Android 4.2 破解了我的加密/解密代码并且提供的解决方案不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆