Security "Crypto" provider deprecated in Android N

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:

SecretKey key = new SecretKeySpec(keyBytes, "AES");

EXAMPLE:

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 调低一些来掩盖崩溃,但这个坑早晚总是要填的。所以让我们来看看该怎么解决这个问题。

三.解决问题

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

/*辅助解密由被废弃的逻辑加密出来的数据的工具类*/
 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:

   // 给出字符串的密码
   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去???


原文发布于微信公众号 - Android历练记(gh_db8538619cdd)

原文发表时间:2017-06-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏移动端开发

iOS加密算法总结

常用加密算法: ---- DES:Data Encryption Standard,即数据加密算法,它是IBM公司于1975年研究成功并公开发表的。 DES(数...

55012
来自专栏三好码农的三亩自留地

浅析 RxJava 2.x 线程调度

为了说明原理,排除干扰,这里用了最简单的逻辑。产生一个字符串“Hello rxJava”, 然后在监听中打印log,subscribeOn(Schedulers...

961
来自专栏窗户

python使用rsa库做公钥解密(网上别处找不到)

  使用RSA公钥解密,用openssl命令就是openssl rsautl -verify -in cipher_text -inkey public.pem...

4739
来自专栏BaronTalk

RxJava系列二(基本概念及使用介绍)

前言 上一篇的示例代码中大家一定发现了Observable这个类。从纯Java的观点看,Observable类源自于经典的观察者模式。RxJava的异步实现正是...

36610
来自专栏Scott_Mr 个人专栏

RxSwift 系列(三) -- Combination Operators

3657
来自专栏web开发

前端AES的加密和解密

在工作的过程中,经常要对一些数据做一些加密,当然有复杂的加密和简单的加密,也有对称加密等等。总之加密的方式有很多。今天在这里,我只是简单的分享一个我最近遇到的加...

1.6K8
来自专栏用户2442861的专栏

Qt学习之路_6(Qt局域网聊天软件)

http://www.cnblogs.com/tornadomeet/archive/2012/07/04/2576355.html

1431
来自专栏JavaEE

spring和springboot中加密连接数据库的信息前言:一、spring中加密连接数据库的信息:二、springboot项目中加密数据库连接信息:总结:

在实际开发中,一些关键的信息肯定是要加密的,否则就太不安全了。比如连接数据库的用户名和密码,一般就需要加密。接下来就看看spring项目和spring boot...

2183
来自专栏Java技术栈

JSON Web Token (JWT)生成Token及解密实战。

昨天讲解了JWT的介绍、应用场景、优点及注意事项等,今天来个JWT具体的使用实践吧。 从JWT官网支持的类库来看,jjwt是Java支持的算法中最全的,推荐使用...

8865
来自专栏一名合格java开发的自我修养

网络安全

对称密钥中,加解密双方难以使用相同密钥,难以事先确定使用一样的密钥。如果网上传输密钥,也会被人截取(截取后,该信息不会发给接收方,只能由接收方发)知道的,恶意者...

1113

扫码关注云+社区

领取腾讯云代金券