NSo*_*uth 8 random android cryptography secure-random android-7.0-nougat
用户可以购买我的应用程序的"专业版".当他们这样做时,我按如下方式存储和验证他们的购买.
SecureRandom.getInstance("SHA1PRNG", "Crypto")
- 这就是问题!所以,不是最好的系统,但是对于我的简陋应用程序来说,所有内容都是模糊不清的.
问题是SecureRandom.getInstance("SHA1PRNG", "Crypto")
在N 上失败,因为不支持"加密".我了解到依赖于特定的提供程序是不好的做法,并且N不支持加密.哎呀.
所以我遇到了一个问题:我依赖于值种子对的加密来始终具有相同的输出.Android N不支持我使用的加密提供程序,因此我不知道如何确保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();
}
Run Code Online (Sandbox Code Playgroud)
我最近与Android安全团队就此进行了讨论.
在Android N中,SHA1PRNG已被删除,因为我们没有安全的实现.具体来说,.setSeed(long)
在请求PRNG输出之前调用会替换SecureRandom实例中的所有熵.
长期以来,这种行为一直被指向安全性失败(读取:经常导致应用程序中的细微错误),因此我们选择在更换SecureRandom提供程序时不复制它.
如果您需要PRNG,那么就使用new SecureRandom()
.
那就是说... SecureRandom()并不是设计用作密钥派生函数,就像你在你的例子中所做的那样.请不要这样做!相反,使用可通过的算法,如PBKDF2 SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
.
我们一直在警告开发人员.请看这些帖子:
如果您真的需要SHA1PRNG,即使在所有这些之后...然后解决方法是将实现复制到Android源代码中,例如在他的答案中提到的@ artjom-b.
但请注意,如果您在迁移到PBKDF2或类似设备时需要兼容性,请执行此操作.
使用PRNG(例如SecureRandom)来确定性地导出数据通常是一个坏主意,因为存在更改中断的历史记录。使用特定的实现并将其包含在您的应用中总是一个好主意。您可以只复制实施代码。
SecureRandom.getInstance("SHA1PRNG", "Crypto");
查找org.apache.harmony.security.provider.crypto.CryptoProvider
Android 5.1.1中的“加密”提供程序。它重定向到org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl
作为实际的实现。您可以使用其他程序包轻松地将代码复制到您的项目中,并确保遵守代码许可证。
然后,您可以像这样使用它:
sr = new SecureRandom(new your.pkg.SHA1PRNG_SecureRandomImpl(), null);
Run Code Online (Sandbox Code Playgroud)
根据代码,不使用第二个provider参数,但是您可以创建一个虚拟provider。
从某些种子生成密钥的正确方法是使用密钥派生函数(KDF)。如果seed
是类似密码的,那么当指定许多迭代时,PBKDF2是一个很好的KDF。如果seed
是类似密钥的,则建议使用类似HKDF的KBKDF。