SecureRandom 提供程序“Crypto”在 Android N 中无法确定性地生成密钥
Posted
技术标签:
【中文标题】SecureRandom 提供程序“Crypto”在 Android N 中无法确定性地生成密钥【英文标题】:SecureRandom provider "Crypto" unavailable in Android N for deterministially generating a key 【发布时间】:2016-08-17 05:20:08 【问题描述】:用户可以购买我的应用的“专业版”版本。当他们这样做时,我按如下方式存储并验证他们的购买。
结合用户的 UUID 和另一个唯一字符串。 然后使用静态种子对生成的字符串进行加密。 我使用SecureRandom.getInstance("SHA1PRNG", "Crypto")
执行此操作-这就是问题所在!
生成的加密字符串就是“解锁码”。
因此,我始终知道预期用户的唯一解锁码值。
当用户购买“Pro”时,我将“解锁码”存储在数据库中。
我通过查看数据库中存储的“解锁码”是否与基于用户的唯一信息的预期码匹配来检查用户是否拥有“Pro”。
所以,这不是最好的系统,但对于我不起眼的应用来说,一切都已经足够模糊了。
问题是SecureRandom.getInstance("SHA1PRNG", "Crypto")
在 N 上失败,因为不支持“加密”。 I have learned that relying on specific providers is bad practice and Crypto is not supported on N。哎呀。
所以我有一个问题:我依靠值种子对的加密来始终具有相同的输出。 android N 不支持我使用的加密提供程序,所以我不支持知道如何确保 N 上的加密输出与其他设备上的相同。
我的问题:
-
是否可以在我的 APK 中包含“加密”以使其始终可用?
在 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();
【问题讨论】:
@ArtjomB.,谢谢。我很难弄清楚如何在我的应用程序中“包含”加密提供程序。看起来提供程序是由系统提供的,所以也许我的应用程序必须安装提供程序?我不知道那会怎样。至于专门的图书馆,你能举个例子吗?我不明白你的意思。 这是他们第二次弄乱 Crypto 库(我相信第一次是 JellyBean 版本之一),彻底破坏了应用程序,给开发人员和用户带来了很多麻烦。因此,我将使用一个不依赖于任何内置于 Android 中的 Crypto 库的加密库。每隔几年就避免这些可笑的并发症很不幸,但有必要。 【参考方案1】:我最近与 Android 安全团队讨论过这个问题。
在 Android N 中,SHA1PRNG 已被移除,因为我们没有对其进行安全实现。具体来说,在请求 PRNG 输出之前调用 .setSeed(long)
会替换 SecureRandom 实例中的所有熵。
这种行为长期以来一直被认为是安全故障(阅读:经常导致应用程序中的细微错误),因此我们选择在替换 SecureRandom 提供程序时不复制它。
如果您需要 PRNG,则只需使用 new SecureRandom()
。
也就是说... SecureRandom() 并非旨在用作密钥派生函数,正如您在示例中所做的那样。请不要这样做!相反,请使用 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 或类似软件时需要兼容性时才这样做。
【讨论】:
嗨特雷弗。首先,很高兴您不使用SecureRandom
。我在这里与getRawKey
斗争了很长时间。我正在尝试为类似问题重新创建功能。您能否确认基于 Harmony 的源在不同版本之间没有改变(当然,我说的是基于 Harmony 的 deterministic 版本,使用了setSeed
)。我确实看过源代码,我必须承认,我非常害怕。然后,我再次责怪 SUN 没有以任何方式指定算法。【参考方案2】:
使用诸如 SecureRandom 之类的 PRNG 来确定性地导出数据通常是一个坏主意,因为存在破坏性更改的历史。使用特定的实现并将其包含在您的应用程序中始终是一个好主意。在您的情况下,可以只复制实现代码。
SecureRandom.getInstance("SHA1PRNG", "Crypto");
查找“加密”提供程序 org.apache.harmony.security.provider.crypto.CryptoProvider
in Android 5.1.1。它重定向到org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl
作为实际实现。您可以轻松地将代码复制到不同包下的项目中,并确保遵守代码许可。
那么你可以这样使用它:
sr = new SecureRandom(new your.pkg.SHA1PRNG_SecureRandomImpl(), null);
根据code 不使用第二个提供程序参数,但您可以创建一个虚拟提供程序。
从某个种子生成密钥的正确方法是使用密钥派生函数 (KDF)。如果seed
类似于密码,那么当指定大量迭代时,PBKDF2 是一个很好的 KDF。如果seed
是key-like,那么推荐使用类似HKDF的KBKDF。
【讨论】:
如果这不是您正在寻找的兼容性级别,您可以在源代码中切换到其他 Android 版本的不同标签。 我在这里有点困惑。他们不会删除 "javax.crypto.*" 吗? Crypto表示java / org / apache / Harmony / security / provider / crypto / ? @NandanKaushikDutta 我不确定你的意思。如果删除“javax.crypto”包,我会感到非常惊讶,因为这样一来,Google 的 Java 和 Oracle(和其他)的 Java 之间就没有加密兼容性。似乎只有“加密”提供程序将与特定的“SHA1PRNG”算法一起被删除。 是的。我在 Android N Emulator 上试过,只删除了“加密”提供程序。【参考方案3】:我为 CryptoProvider 添加了一个类,您可以将 SecureRandom.getInstance("SHA1PRNG", "Crypto"); 替换为 SecureRandom.getInstance("SHA1PRNG", new CryptoProvider());
您可以参考以下链接获取解决方案,它对我有用;
Security "Crypto" provider deprecated in Android N
【讨论】:
以上是关于SecureRandom 提供程序“Crypto”在 Android N 中无法确定性地生成密钥的主要内容,如果未能解决你的问题,请参考以下文章