android N中启用了安全“加密”提供程序


针对应用和游戏开发者的最新Android和Google Play新闻。
Android N中弃用了安全“加密”提供程序
2016年6月9日
软件工程师Sergio Giro发布


如果您的Android应用程序使用来自加密提供程序的SHA1PRNG算法派生密钥,则必须开始使用真正的密钥派生函数,并可能重新加密数据。


Java加密体系结构允许开发人员使用以下调用创建类的实例,如密码或伪随机数生成器:
SomeClass.getInstance("SomeAlgorithm", "SomeProvider");
或者干脆:
SomeClass.getInstance("SomeAlgorithm");
例如,
Cipher.getInstance(“AES/CBC/PKCS5PADDING”);
SecureRandom.getInstance(“SHA1PRNG”);
在Android上,我们不建议指定提供者。一般来说,只有在提供程序包含在应用程序中或者应用程序能够处理可能的ProviderNotFoundException时,才可以调用指定提供程序的Java Cryptography Extension(JCE)API。


不幸的是,许多应用程序依赖于现在删除的“加密”提供商提供的密钥派生的反模式。


该提供者仅为SecureRandom实例提供了算法“SHA1PRNG”的实现。问题是SHA1PRNG算法不具有密码强度。对于对细节感兴趣的读者,由Yongge Want和Tony Nicol撰写的 基于统计距离的伪随机序列测试和PHP和Debian OpenSSL 8.1节的实验表明,以二进制形式考虑的“随机”序列偏向于返回0,并且偏差根据种子而恶化。


因此, 在Android N中,我们不赞成SHA1PRNG算法和Crypto提供者的实现。我们之前已经介绍了几年前使用SecureRandom进行密钥派生时 使用加密技术安全存储凭证的问题。但是,鉴于其持续使用,我们将在此重新审视。


此提供程序的一个常见但不正确的用法是通过将密码用作种子来派生加密密钥。SHA1PRNG的实现有一个错误,如果在获取输出之前调用了setSeed(),则会导致确定性。这个bug被用来通过提供一个密码作为种子来获得密钥,然后使用密钥的“随机”输出字节(在这个句子中的“随机”意味着“可预测的和密码学上的弱”)。然后可以使用这样的密钥来加密和解密数据。


在下文中,我们将解释如何正确导出密钥,以及如何解密使用不安全密钥加密的数据。还有一个 完整的示例,其中包括使用已弃用的SHA1PRNG功能的帮助程序类,其唯一目的是解密否则将不可用的数据。


密钥可以通过以下方式导出:


如果你从磁盘读取AES密钥,只需存储实际的密钥,不要通过这个奇怪的舞蹈。您可以通过执行字节获得AES用法的SecretKey:
SecretKey key = new SecretKeySpec(keyBytes, "AES");
如果您使用密码来派生密钥, 请遵循Nikolay Elenkov出色的教程,并注意一条好的经验法则,即盐的大小应与关键输出的大小相同。它看起来像这样:
   
 
/ *用户输入密码:* / String password = “password” ;  
     
   / *将这些内容存储在用于派生密钥的磁盘上:* / 
int iterationCount = 1000 ; int saltLength = 32 ; 
//字节; 应的尺寸相同为输出(256 / 8 = 32 )
INT keyLength = 256 ; 
// AES-256的256位,AES-128的128位等 
  byte [] salt ;
 //应该是saltLength    
      
   / *首次创建密钥时,用以下方法获得一个salt:* /
 SecureRandom random = new SecureRandom (); 
byte [] salt = new byte [ saltLength ];   
   random.nextBytes (salt );  
       
   / *使用此推导从密码密钥:* / 
KeySpec keySpec = new PBEKeySpec (password.toCharArray (),salt,  
              iterationCount ,keyLength ); 
SecretKeyFactory keyFactory = SecretKeyFactory 。getInstance (“PBKDF2WithHmacSHA1” ); byte [] keyBytes = keyFactory.generateSecret (keySpec) .getEncoded (); 
SecretKey key = new SecretKeySpec (keyBytes ,“AES” );                      
而已。你不应该需要任何其他东西。

为了简化转换数据,我们介绍了使用不安全密钥加密数据的开发人员的情况,该密钥是每次都从密码派生的。您可以使用辅助类 InsecureSHA1PRNGKeyDerivator中 的 示例应用程序派生密钥。

 
private static SecretKey deriveKeyInsecurely (String password,int 
 keySizeInBytes ){ byte [] passwordBytes = password.getBytes (StandardCharsets 。US_ASCII ); 

return new SecretKeySpec (InsecureSHA1PRNGKeyDerivator 。deriveInsecureKey (  

                     passwordBytes ,keySizeInBytes ),“AES” ); }   
   
然后,您可以使用安全派生的密钥重新对您的数据进行加密,如上所述,过上幸福的生活。


注1:作为保持应用程序正常工作的临时措施,我们决定仍然为面向SDK版本23的应用程序创建实例,该版本是Marshmallow的SDK版本或更少。请不要依赖Android SDK中的Crypto提供程序,我们的计划是在将来彻底删除它。


注2:由于系统的许多部分都假定存在SHA1PRNG算法,因此当请求SHA1PRNG实例并且未指定提供者时,我们将返回一个OpenSSLRandom实例,该实例是从OpenSSL派生的随机数的强大来源。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值