用户可以购买我的应用程序的“专业版”。当他们这样做时,我会按如下方式存储和验证他们的购买。
- 将用户的 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 上的加密输出与其他设备上的相同。
我的问题:
- 是否可以在我的 APK 中包含“Crypto”以便它始终可用?
- 在 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")
获得。
一段时间以来,我们一直在就此警告开发人员。请查看这些帖子:
- Android Developers Blog: Using Cryptography to Store Credentials Safely
- Android 4.2 broke my encrypt/decrypt code and the provided solutions don't work
如果你真的需要 SHA1PRNG,即使在所有这些之后...... 那么解决方法是从 Android 源代码中复制实现,就像他的回答中提到的@artjom-b。 p>
但是,请仅在迁移到 PBKDF2 或类似版本时需要兼容性时才这样做。
关于android - SecureRandom 提供程序 "Crypto"在 Android N 中无法确定地生成 key ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36813098/