博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Keystore 对称-非对称加密
阅读量:6971 次
发布时间:2019-06-27

本文共 7659 字,大约阅读时间需要 25 分钟。

Android数据加密:

Android 提供了 KeyStore 等可以长期存储和检索加密密钥的机制,Android KeyStore 系统特别适合于存储加密密钥。

“AndroidKeyStore” 是 KeyStore 的一个子集,存进 AndroidKeyStore 的 key 将受到签名保护,并且这些 key 是存在系统里的,而不是在 App 的 data 目录下,依托于硬件的 KeyChain 存储,可以做到 private key 一旦存入就无法取出,

每个 App 自己创建的 key,别的应用是访问不到的。

它提供了限制何时以何种方式使用密钥的方法,比如使用密钥时需要用户认证或限制密钥只能在加密模式下使用。

一个应用程式只能编辑、保存、取出自己的密钥。

App可以生成或者接收一个公私密钥对,并存储在Android的Keystore系统中。公钥可以用于在应用数据放置到特定文件夹前对数据进行加密,私钥可以在需要的时候解密相应的数据。

作用:

KeyStore 适用于生成和存储密钥,这些密钥可以用来加密运行时获取到的数据,比如运行时,用户输入的密码,或者服务端传下来的 token。

操作方式

  • 存:使用 加密信息后存入
  • 取:从取出信息並使用 解密

建议做法

1. 使用对称式加解密,但只能在Api Level 23+使用

对称式加解密(AES)速度较快,但是对称式的Key若要存在KeyStore裡,Api level一定要在23以上才支持,23以下是无法存入KeyStore的,非对称式的Key則不在此限。

2. 想兼容各Api版本(23以下也能用)

  • 若要存取的東西不多、字串長度也不長:直接使用非对称式加解密即可
  • 若要存取的東西很多或字串長度很長:由於非对称式加解密速度较慢,使用非对称式+对称式加解密可以解決此問題。

考慮到加解密效能、版本兼容,下面會介紹用非对称式+对称式來加解密。

 

KeyStore非对称+对称式加解密流程

  1. 使用KeyStore产生随机的RSA Key;
  2. 产生AES Key,并用RSA Public Key加密后存入SharedPrefs;
  3. 从SharedPrefs取出AES Key,並用RSA Private Key解密,用這把AES Key來加解密信息;

 

主流的加密方式有:(对称加密)、DES        (非对称加密)RSA、DSA

工作模式:

DES一共有:

电子密码本模式()、加密分组链接模式()、加密反馈模式()、输出反馈模式();

一共有:

电子密码本模式()、加密分组链接模式()、加密反馈模式()、输出反馈模式()、计数器模式(),伽罗瓦计数器模式()

PKCS5Padding是填充模式,还有其它的填充模式;

对于初始化向量iv: 初始化向量参数,AES 为16bytes. DES 为8bytes

 

1 private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";2 private static final String AES_MODE = "AES/GCM/NoPadding";3 private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";4 5 private static final String KEYSTORE_ALIAS = "KEYSTORE_DEMO";6 7 KeyStore mKeyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);8 mKeyStore.load(null);

(1)产生随机的RSA Key

产生RSA Key会使用到:

其中在Api 23以上已經Deprecated了;

Api level 23以上改使用;

1 private void genKeyStoreKey(Context context) throws Exception {2         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {3             generateRSAKey_AboveApi23();4         } else {5             generateRSAKey_BelowApi23(context);6         }7     }

api23 以上使用 :

1 @RequiresApi(api = Build.VERSION_CODES.M) 2     private void generateRSAKey_AboveApi23() throws Exception { 3         KeyPairGenerator keyPairGenerator = KeyPairGenerator 4                 .getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER); 5  6  7         KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec 8                 .Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 9                 .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)10                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)11                 .build();12 13         keyPairGenerator.initialize(keyGenParameterSpec);14         keyPairGenerator.generateKeyPair();15 16     }

api23 以下使用 :

1 private void generateRSAKey_BelowApi23(Context context) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { 2         Calendar start = Calendar.getInstance(); 3         Calendar end = Calendar.getInstance(); 4         end.add(Calendar.YEAR, 30); 5  6         KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context) 7                 .setAlias(KEYSTORE_ALIAS) 8                 .setSubject(new X500Principal("CN=" + KEYSTORE_ALIAS)) 9                 .setSerialNumber(BigInteger.TEN)10                 .setStartDate(start.getTime())11                 .setEndDate(end.getTime())12                 .build();13 14         KeyPairGenerator keyPairGenerator = KeyPairGenerator15                 .getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER);16 17         keyPairGenerator.initialize(spec);18         keyPairGenerator.generateKeyPair();19     }

(2)产生AES Key后, 并用RSA Public Key加密后存入SharedPrefs

1 private void genAESKey() throws Exception { 2         // Generate AES-Key 3         byte[] aesKey = new byte[16]; 4         SecureRandom secureRandom = new SecureRandom(); 5         secureRandom.nextBytes(aesKey); 6  7         // Generate 12 bytes iv then save to SharedPrefs 8         byte[] generated = secureRandom.generateSeed(12); 9         String iv = Base64.encodeToString(generated, Base64.DEFAULT);10         prefsHelper.setIV(iv);12 13         // Encrypt AES-Key with RSA Public Key then save to SharedPrefs14         String encryptAESKey = encryptRSA(aesKey);15         prefsHelper.setAESKey(encryptAESKey);16     }

  1] 加密存储:使用RSA Public Key 加密 AES Key,存入缓存中。

  2]  解密使用:使用RSA Private Key 解密 得到 AES Key。

1 private String encryptRSA(byte[] plainText) throws Exception { 2         PublicKey publicKey = keyStore.getCertificate(KEYSTORE_ALIAS).getPublicKey(); 3  4         Cipher cipher = Cipher.getInstance(RSA_MODE); 5         cipher.init(Cipher.ENCRYPT_MODE, publicKey); 6  7         byte[] encryptedByte = cipher.doFinal(plainText); 8         return Base64.encodeToString(encryptedByte, Base64.DEFAULT); 9    }11 12     private byte[] decryptRSA(String encryptedText) throws Exception {13         PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS, null);14 15         Cipher cipher = Cipher.getInstance(RSA_MODE);16         cipher.init(Cipher.DECRYPT_MODE, privateKey);17 18         byte[] encryptedBytes = Base64.decode(encryptedText, Base64.DEFAULT);19         byte[] decryptedBytes = cipher.doFinal(encryptedBytes);20 21         return decryptedBytes;22     }

获取AES :

1 private SecretKeySpec getAESKey() throws Exception {2         String encryptedKey = prefsHelper.getAESKey();3         byte[] aesKey = decryptRSA(encryptedKey);4 5         return new SecretKeySpec(aesKey, AES_MODE);6     }

再使用AES 加解密内容:

对于:Cipher 初始化

1 //实例化加密类,参数为加密方式,要写全2 Cipher cipher = Cipher.getIntance("AES/CBC/PKCS5Padding");3 4 //初始化,此方法可以采用三种方式,按服务器要求来添加。5 //(1)无第三个参数6 //(2)第三个参数为SecureRandom random = new SecureRandom();7 // 中random对象,随机数。(AES不可采用这种方法)8 //(3)第三个参数:IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes); 9 cipher.init(Cipher.ENCRYPT_MODE,  keySpec,  ivSpec/random);

具体使用:

1 /** 2      * AES Encryption 3      * @param plainText: A string which needs to be encrypted. 4      * @return A base64's string after encrypting. 5      */ 6     private String encryptAES(String plainText) throws Exception { 7         Cipher cipher = Cipher.getInstance(AES_MODE); 8         cipher.init(Cipher.ENCRYPT_MODE, getAESKey(), new IvParameterSpec(getIV())); 9 10         // 加密過後的byte11         byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());12 13         // 將byte轉為base64的string編碼14         return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);15     }17 18     private String decryptAES(String encryptedText) throws Exception {19         // 將加密過後的Base64編碼格式 解碼成 byte20         byte[] decodedBytes = Base64.decode(encryptedText.getBytes(), Base64.DEFAULT);21 22         // 將解碼過後的byte 使用AES解密23         Cipher cipher = Cipher.getInstance(AES_MODE);24         cipher.init(Cipher.DECRYPT_MODE, getAESKey(), new IvParameterSpec(getIV()));25 26         return new String(cipher.doFinal(decodedBytes));27     }

iv 初始化向量

1 private byte[] getIV() {2      String prefIV = prefsHelper.getIV();3      return Base64.decode(prefIV, Base64.DEFAULT);4 }

 

关于RSA:

使用RSA加解密时,在较低版本的手机上可能无法选择OAEP(最优非对称加密填充,RSA的加密解密是基于OAEP的)這個模式;

因此可以改使用RSA_PKCS1_PADDING模式,使用这个模式的話,输入必须比RSA的Key至少11個字节,如果需要被加密的字串过长的话,可以在产生Key时指定Key Size长度,或是将字串分段加密。

以预设Key Size = 2048bit(256byte)來说,输入最长只能到256–11=245byte,我們可以透过setKeySize(int keySize)指定Key的长度,但是Key Size越大,加解密时速度就越慢。

1 KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec2         .Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)3         .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)4         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)5         .setKeySize(4096)6         .build();

 

引言:

 

转载地址:http://ylasl.baihongyu.com/

你可能感兴趣的文章
对Linux(Unix)的基础知识归纳
查看>>
自定义可扩展叠加头部的下拉控件
查看>>
转--Java工程师成神之路(2018修订版)
查看>>
SQLServer之Compute/ComputeBy实现数据汇总
查看>>
KMP算法
查看>>
回车提交
查看>>
代码基本素质
查看>>
关于数据库的索引
查看>>
Apache POI使用详解(摘抄)
查看>>
PAT_A1076#Forwards on Weibo
查看>>
黑马程序员博客-------面向对象
查看>>
Python 类 面向对象(Classes)
查看>>
如何完全卸载(Mac&Windows)office 365 ProPlus
查看>>
android实战开发02
查看>>
网络中的连接设备
查看>>
业务逻辑:完成基于CRM地址完全匹配的自动分单业务逻辑
查看>>
2019校招面经大汇总
查看>>
[CSS]滚动条样式设置
查看>>
转://oracle 软件的收费模式
查看>>
JDBC
查看>>