
原文来自: Android Developers Blog —— Security “Crypto” provider deprecated in Android N
如果你的安卓应用程序使用的是SHA1PRNG算法从加密提供者那里取得的密钥,你必须开始使用一个真正的密钥导出函数,然后可能需要重新加密你的数据。
Java加密体系结构允许开发者创建像密码一样的一个类的实例,或者是生成伪随机数,像这样去调用它:
1 | SomeClass.getInstance("SomeAlgorithm", "SomeProvider"); |
或者更简单通熟些:
1 | SomeClass.getInstance("SomeAlgorithm"); |
对于这个实例,
1 | Cipher.getInstance(“AES/CBC/PKCS5PADDING”); |
在Android上,我们不建议指定提供者。一般来说,任意调用Java加密扩展(JCE)API指定一个提供者,如果这个提供者是包括在应用程序里,或者说这个应用程序能够处理可能出现的ProviderNotException异常,也应该仅仅能完成这些。
不幸的是,很多应用程序都依赖于现在已经被移除了的”加密”提供者,对于一个密钥派生出来的反模式
这个提供者只能被提供一些为SecureRandom实例去实现的“SHA1PRNG”算法接口,问题是SHA1PRNG算法不是强加密,在使用基于PHP和Debian OpenSSL的伪随机序列和实验的统计距离的测试,8.1那部分,Yongge Want 和 Tony Nicol,指出该”随机”数列,以二进制形式考虑,朝0偏移返回,而且该偏移恶化的程度取决于种子。
结果是,在Android N中我们干脆弃用依赖于以“SHA1PRNG”算法去实现的接口和该加密提供者。我们在几年前使用加密安全存储凭据预先去覆盖使用SecureRandom派生出来的密钥产生的那些问题。然而,鉴于要继续使用它,我们将在这里重温一下。
该提供者有一个常见但是不正确的使用方法,就是通过使用密码作为种子来推导出加密密钥。SHA1PRNG接口有一个漏洞缺陷,如果setSeed()在获得输出之前被调用,那么就使他正确了。这个漏洞缺陷已经被用于密码作为种子派生加密密钥的关键,然后使用“随机”输出关键的密钥(其中,”随机”在这句话里的意思是“可以预测和若加密”),然后这种密钥可以用于加密和解密数据。
接下来,我们解释如何正确的导出密钥,以及如何解密已经进行不安全密钥加密的数据。这儿也有一个完整的例子,包括了一个帮助类去使用已经弃用SHA1PRNG功能,解密数据的唯一目的将不可用。
密钥可以通过以下方式得到:
如果你是通过从磁盘上读取一个AES的密钥,只是存储实际的密钥,你可以从通过使用执行字节AES生成SecretKey密钥:
1 | SecretKey key = new SecretKeySpec(keyBytes, "AES"); |
如果您使用密码来获得一个密钥,参照Nikolay Elenkovd的这篇优秀教程,一个好的经验法制是腌制的大小应该和密钥输出的大小一致,它看上去就像是这样:
1 | /* User types in their password: */ |
就是这样,你也不需要做其他的任何事情。
为了使过渡数据更容易,我们覆盖开发者的情况,有一个不安全的密钥加密的数据,它是每次从一个密码那里派生出来的。你可以在示例应用程序中使用帮助类InsecureSHA1PRNGKeyDerivator去派生出密钥:
1 | private static SecretKey deriveKeyInsecurely(String password, int |
然后,你可以使用安全的派生出的密钥按照上面的所述的重新加密数据,并从此过上幸福的生活。
注意1:作为一个临时的措施去维持应用重新工作,我们决定了在目标SDK版本为Mashmallow(API23)及其低版本还是为应用程序创建示例,请不要在Android SDK中存在依赖加密提供者,我们的计划是在未来会将其完全删除。
注意2:因为该系统很多部分都承担了SHA1PRNG算法的存在,当一个SHA1PRNG实例请求和提供者没有指定,我们将返回一个OpenSSLRandom的实例,这是从OpenSSL的衍生出随机数的一个强有力的来源。
以上是对Anroid 官网博客文章的翻译,如果不足,欢迎指正,谢谢。