前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Security "Crypto" provider deprecated in Android N

Security "Crypto" provider deprecated in Android N

作者头像
Anymarvel
发布2018-10-22 11:31:43
5310
发布2018-10-22 11:31:43
举报
文章被收录于专栏:Android开发实战Android开发实战

Android:7.0 后加密库 Crypto 被废弃

一.问题描述

我们知道加密[算法]都是需要密钥的,比如 AES 算法支持128 比特、192 比特和 256 比特三种长度的密钥,通常这些密钥会被转化成字节数组明文写在代码中或者写入成 KeyStore 文件。如果你是直接使用这些密钥的话是不会有任何问题的,但是有的时候我们需要通过一个字符串格式的密码来生成密钥。 我们需要可逆的加密方式的时间,在Android中一般会使用Crypto这个库里面的一些函数进行加密,但是,使用targetSdkVersion为25进行编译运行在Android7.0的手机上额时间,你会发现,首次安装加密的串一定是空的,错误如下所示。

上述的链接为http://android-developers.blogspot.com/2016/06/security-crypto-provider-deprecated-in.html 指向的是官网说明:Android:7.0 后加密库 Crypto 被废弃 Android Developers Blog 官方版本的说明文档

二.原始代码(被弃用代码书写方式)

一般情况下可逆的加密方式网上有很多种,百度可以出来一大堆,这里使用官方示例 Keys can be derived in the following way: If you're reading an AES key from disk, just store the actual key and don't go through this weird dance. You can get a SecretKey for AES usage from the bytes by doing:

代码语言:javascript
复制
SecretKey key = new SecretKeySpec(keyBytes, "AES");

EXAMPLE:

代码语言:javascript
复制
public static final String DEPREACATED_SECURE_ALGORITHM_SHA1PRNG = "SHA1PRNG";    public static final String DEPREACATED_SECURE_PROVIDER_CRYPTO = "Crypto";    /**
     * 按照指定编码从字符串中生成指定长度的密钥 key。
     *
     * @param password
     * @param charset
     * @param keyBitLen
     * @return
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     */
    @Deprecated
    public static byte[] deriveKeyDeprecated(String password, @Nullable Charset charset, int keyBitLen) throws NoSuchProviderException, NoSuchAlgorithmException {
        SecureRandom secureRandom = SecureRandom.getInstance(DEPREACATED_SECURE_ALGORITHM_SHA1PRNG, DEPREACATED_SECURE_PROVIDER_CRYPTO);        //在随机数生成器中将密码的字符串设为种子换算出最终的密钥key,异常就是在这里发生的
        secureRandom.setSeed(password.getBytes(charset != null ? charset : Charset.defaultCharset()));

        KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
        keyGenerator.init(keyBitLen, secureRandom);
        SecretKey secretKey = keyGenerator.generateKey();        return secretKey.getEncoded();
    }

可以看到我们将密码作为随机数生成器的种子换算出密钥 key,这种做法已经被认定为是不安全的。官方开发人员在 Axndroid N 上已经将相关的 Crypto provider 和 SHA1PRNG 算法同时废弃掉了,并计划在后续的 SDK 中完全移除相关的库。

当然你可以直接使用密钥来绕过这个问题,或者将 targetSdkVersion 调低一些来掩盖崩溃,但这个坑早晚总是要填的。所以让我们来看看该怎么解决这个问题。

三.解决问题

首先我们看下官方的解决方案

代码语言:javascript
复制
/*辅助解密由被废弃的逻辑加密出来的数据的工具类*/
 private static SecretKey deriveKeyInsecurely(String password, int
 keySizeInBytes) {  
    byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII);  
    return new SecretKeySpec(  
            InsecureSHA1PRNGKeyDerivator.deriveInsecureKey(  
                     passwordBytes, keySizeInBytes),  
            "AES");  
 }

上面的方法是为了辅助解密由被废弃的逻辑加密出来的数据的工具类,当前中文注解是后期加上去的。 我们也可以直接使用java.security包中的内容进行加密算法的重构,其实就是几句简单的话 EXAMPLE:

代码语言:javascript
复制
   // 给出字符串的密码
   String password = "password";  

   // 密钥的比特位数,注意这里是比特位数
   // AES 支持 128、192 和 256 比特长度的密钥
   int keyLength = 256; 
   // 盐值的字节数组长度,注意这里是字节数组的长度
   // 其长度值需要和最终输出的密钥字节数组长度一致
   // 由于这里密钥的长度是 256 比特,则最终密钥将以 256/8 = 32 位长度的字节数组存在
   // 所以盐值的字节数组长度也应该是 32
   int saltLength = 32;   byte[] salt;   // 先获取一个随机的盐值
   // 你需要将此次生成的盐值保存到磁盘上下次再从字符串换算密钥时传入
   // 如果盐值不一致将导致换算的密钥值不同
   // 保存密钥的逻辑官方并没写,需要自行实现
   SecureRandom random = new SecureRandom();  
   byte[] salt = new byte[saltLength];  
   random.nextBytes(salt);  

   // 将密码明文、盐值等使用新的方法换算密钥
   int iterationCount = 1000;
   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");

以上就是修改可逆的加密方式进行正确的解决Security "Crypto" provider deprecated in Android N的问题,若只是为了兼容老版本也可使用官网中的例子。

Note 1: as a temporary measure to keep apps working, we decided to still create the instance for apps targeting SDK version 23, the SDK version for Marshmallow, or less. Please don't rely on the presence of the Crypto provider in the Android SDK, our plan is to delete it completely in the future.

Note 2: Because many parts of the system assume the existence of a SHA1PRNG algorithm, when an instance of SHA1PRNG is requested and the provider is not specified we return an instance of OpenSSLRandom, which is a strong source of random numbers derived from OpenSSL.

官方计划将完全删除Crypto和SHA1PRNG,一般情况下许多系统假定SHA1PRNG算法存在,在去除后将返回一个OpenSSLRandom实例,实例是根据OpenSSL派生出的。

目前使用targetSdkVersion低版本的方式进行掩盖,但个别手机也会出现首次加解密为空的情况,直接闪退或者重启,并不会进行crash的报错,即使是报错也是某native字段为空。亲们赶紧兼容Nougat(牛轧糖)(Android N) 接下来准备拥抱8.0Android O吧

PS:仔细查看代码中是否使用了Crypto这个库,也就是javax.crypto这个包下的内容都要仔细验证。 你是不是想说??

还不赶紧改Bug去???

还不赶紧改Bug去???

还不赶紧改Bug去???


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-06-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android历练记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.问题描述
  • 二.原始代码(被弃用代码书写方式)
  • EXAMPLE:
  • 三.解决问题
  • 还不赶紧改Bug去???
  • 还不赶紧改Bug去???
  • 还不赶紧改Bug去???
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档