android - SecureRandom 提供程序 "Crypto"在 Android N 中无法确定地生成 key

标签 android random cryptography android-7.0-nougat secure-random

用户可以购买我的应用程序的“专业版”。当他们这样做时,我会按如下方式存储和验证他们的购买。

  • 将用户的 UUID 和另一个唯一字符串组合起来。
  • 然后使用静态种子对生成的字符串进行加密。 我使用 SecureRandom.getInstance("SHA1PRNG", "Crypto")- 这就是问题所在!
  • 生成的加密字符串就是“解锁码”。
  • 因此,我总是知道预期用户的唯一解锁代码值。
  • 当用户购买“Pro”时,我将“解锁码”存储在数据库中。
  • 我通过查看数据库中存储的“解锁代码”是否与基于他们的独特信息的预期代码匹配来检查用户是否拥有“Pro”。

所以,这不是最好的系统,但对于我不起眼的应用程序来说,一切都足够混淆了。

问题是 SecureRandom.getInstance("SHA1PRNG", "Crypto") 在 N 上失败,因为不支持“Crypto”。 I have learned that relying on specific providers is bad practice and Crypto is not supported on N .哎呀。

所以我有一个问题:我依赖于值种子对的加密来始终具有相同的输出。Android N 不支持我使用的加密提供程序,所以我不知道如何确保 N 上的加密输出与其他设备上的相同。

我的问题:

  1. 是否可以在我的 APK 中包含“Crypto”以便它始终可用?
  2. 在 Android N 上加密值-种子对时,我能否以其他方式确保相同的输出?

我的代码:

public static String encrypt(String seed, String cleartext) throws Exception {
    byte[] rawKey = getRawKey(seed.getBytes(), seed);
    byte[] result = encrypt(rawKey, cleartext.getBytes());
    return toHex(result); // "unlock code" which must always be the same for the same seed and clearText accross android versions
}

private static byte[] getRawKey(byte[] seed, String seedStr) throws Exception {
    SecureRandom sr;
    sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");  // what used to work
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    sr.setSeed(seed);
    kgen.init(128, sr); 
    SecretKey skey = kgen.generateKey();
    byte[] raw = skey.getEncoded();
    return raw;
}

private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(clear);
    return encrypted;
}

public static String toHex(byte[] buf) {
    if (buf == null)
        return "";
    StringBuffer result = new StringBuffer(2 * buf.length);
    for (int i = 0; i < buf.length; i++) {
        appendHex(result, buf[i]);
    }
    return result.toString();
}

最佳答案

我最近与 Android 安全团队讨论过这个问题。

在 Android N 中,SHA1PRNG 已被删除,因为我们没有它的安全实现。具体来说,在从 PRNG 请求输出之前调用 .setSeed(long) 会替换 SecureRandom 实例中的所有熵。

此行为长期以来一直被认为是安全故障(阅读:经常导致应用程序中出现细微错误),因此我们选择在替换 SecureRandom 提供程序时不复制它。

如果您需要 PRNG,则只需使用 new SecureRandom()

也就是说... SecureRandom() 并非设计用作 key 派生函数,正如您在示例中所做的那样。请不要这样做!相反,使用 PBKDF2 等算法,可通过 SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") 获得。

一段时间以来,我们一直在就此警告开发人员。请查看这些帖子:

如果你真的需要 SHA1PRNG,即使在所有这些之后...... 那么解决方法是从 Android 源代码中复制实现,就像他的回答中提到的@artjom-b。 p>

但是,请仅在迁移到 PBKDF2 或类似版本时需要兼容性时才这样做。

关于android - SecureRandom 提供程序 "Crypto"在 Android N 中无法确定地生成 key ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36813098/

相关文章:

c# - 为什么这个 Base36 随机字符串不使用 RandomNumberGenerator 随机分布字符

java - 如何在 Java 中使用 SHA-256 散列一些字符串?

java - ANDROID [后退按钮导致崩溃]

javascript - Phonegap 构建、HTML5 音频、Android

python - python中的牌组卡类

java - java中的细粒度身份验证

json - 如何从json中获取相同的hash

php - Android和PHP codeigniter连接的简单代码

java - 组中的单选按钮给出 Null 异常

scala - 如何找到适合 32 位整数的 n 的最大倍数