Android(安卓)数据加密方法 AES DES RSA AndroidKeyStore
Java概述
一、使用SecretKeySpec生成秘钥进行对称加密
一个问题 如果创建密码器的时候,Cipher cipher = Cipher.getInstance(TRANSFORMATION);
TRANSFORMATION = "AES"如果和生成秘钥的算法一样设置为 “AES/CBC/PKCS5PADDING”,就会报下面的错,
java.security.InvalidKeyException: no IV set when one expected
没有理解为什么啊。请路过的大佬指点。。
/** * 对称加密数据 * * 使用SecretKeySpec生成秘钥,用AES/DES方式生成秘钥 * 每次加解密都生成同样的秘钥 */public class SymmetricEncryUtils { //AES ,DES // 算法/模式/补码方式 //与给定的密钥内容相关联的 密钥算法的名称 // private static String ALGRITHM = "AES/CBC/PKCS5PADDING"; private static String ALGRITHM = "DES/CBC/PKCS5Padding"; // private static String TRANSFORMATION = "AES"; private static String TRANSFORMATION = "DES"; public static byte[] generateRandomString() { Random random = new Random(); //AES秘钥规定是16位秘钥 //DES 8bytes byte[] rand = new byte[8]; random.nextBytes(rand); return rand; } private static final byte[] ALIAS = generateRandomString(); public static String encrypt(String content) { try { // 创建秘钥 SecretKeySpec keySpec = new SecretKeySpec(ALIAS, ALGRITHM); // 创建密码器 Cipher cipher = Cipher.getInstance(TRANSFORMATION); // 初始化加密器 cipher.init(Cipher.ENCRYPT_MODE, keySpec); // 加密 return Base64.encodeToString(cipher.doFinal(content.getBytes("UTF-8")), Base64.NO_WRAP); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return ""; } public static String decrypt(String content) { try { // 创建秘钥 SecretKeySpec keySpec = new SecretKeySpec(ALIAS, ALGRITHM); // 创建密码器 Cipher cipher = Cipher.getInstance(TRANSFORMATION); // 初始化解密器 cipher.init(Cipher.DECRYPT_MODE, keySpec); // 解密 return new String(cipher.doFinal(Base64.decode(content, Base64.NO_WRAP)), "UTF-8"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return ""; }}
二、使用keyGenerator 生成一个秘钥,保存在Androidkeystore里面
但是这里面解密要用到加密时候cipher对象获取的iv
encryptIv = cipher.getIV();
难道每个加密数据还要对应保存其加密时候的iv吗?
请大佬指点。
还有KeyGenerator初始化时的Spec对象,Android6.0以上可以使用 KeyGenParameterSpec来生成,但是低版本用哪个对象生成呢?搜了半天没解决。
/** * 通过AES加密方式,用KeyGenerator生成秘钥,保存在Android Keystore中 * 对数据进行加解密 * * 1、创建秘钥,保存在AndroidKeystore里面,秘钥别名为alias * 2、创建并初始化cipher对象,获取秘钥,对数据进行加解密 */public class AESKeystoreUtils { private static final String ALIAS = "123"; // 算法/模式/补码方式 private static String TRANSFORMATION = "AES/GCM/NoPadding"; private static byte[] encryptIv; /** * 创建秘钥 */ private static void createKey() { //获取Android KeyGenerator的实例 //设置使用KeyGenerator的生成的密钥加密算法是AES,在 AndroidKeyStore 中保存密钥/数据 final KeyGenerator keyGenerator; AlgorithmParameterSpec spec = null; try { keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); //使用KeyGenParameterSpec.Builder 创建KeyGenParameterSpec ,传递给KeyGenerators的init方法 //KeyGenParameterSpec 是生成的密钥的参数 //setBlockMode保证了只有指定的block模式下可以加密,解密数据,如果使用其它的block模式,将会被拒绝。 //使用了“AES/GCM/NoPadding”变换算法,还需要设置KeyGenParameterSpec的padding类型 //创建一个开始和结束时间,有效范围内的密钥对才会生成。 Calendar start = new GregorianCalendar(); Calendar end = new GregorianCalendar(); end.add(Calendar.YEAR, 10);//往后加十年 //todo 高于6.0才可以使用KeyGenParameterSpec 来生成秘钥,低版本呢? if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { spec = new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setCertificateNotBefore(start.getTime()) .setCertificateNotAfter(end.getTime()) .build(); } else {// spec = new ; } keyGenerator.init(spec); keyGenerator.generateKey(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } } public static String encryptData(String needEncrypt) { if (!isHaveKeyStore()) { createKey(); } try { KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null); SecretKey secretKey = secretKeyEntry.getSecretKey(); //KeyGenParameterSpecs中设置的block模式是KeyProperties.BLOCK_MODE_GCM,所以这里只能使用这个模式解密数据。 Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, secretKey); //ciphers initialization vector (IV)的引用,用于解密 encryptIv = cipher.getIV(); return Base64.encodeToString(cipher.doFinal(needEncrypt.getBytes()), Base64.NO_WRAP); } catch (KeyStoreException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnrecoverableEntryException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); return "空指针异常"; } return ""; } public static String decryptData(String needDecrypt) { if (!isHaveKeyStore()) { createKey(); } try { KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null); SecretKey secretKey = secretKeyEntry.getSecretKey(); //KeyGenParameterSpecs中设置的block模式是KeyProperties.BLOCK_MODE_GCM,所以这里只能使用这个模式解密数据。 Cipher cipher = Cipher.getInstance(TRANSFORMATION); //需要为GCMParameterSpec 指定一个认证标签长度(可以是128、120、112、104、96这个例子中我们能使用最大的128), // 并且用到之前的加密过程中用到的IV。 GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, encryptIv); cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec); return new String(cipher.doFinal(Base64.decode(needDecrypt, Base64.NO_WRAP))); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnrecoverableEntryException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } return ""; } /** * 是否创建过秘钥 * * @return */ private static boolean isHaveKeyStore() { try { KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); KeyStore.Entry keyentry = keyStore.getEntry(ALIAS, null); if (null != keyentry) { return true; } } catch (KeyStoreException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (UnrecoverableEntryException e) { e.printStackTrace(); } return false; }}
三、使用RAS + AndroidKeyStore方式加密数据
通过keypairGenerator生成秘钥对,公钥用于加密数据,私钥用于解密数据。
但是有个问题是:
keyPairGenerator.generateKeyPair();
生成秘钥这一步,涉及到大量的运算?view明显的停顿了一下,log显示:
Skipped 129 frames! The application may be doing too much work on its main thread.
待解决的问题。大佬路过请指示。
public class RSAKeystoreUtils { private static final String AndroidKeyStore = "AndroidKeyStore"; //RSA/ECB/PKCS1Padding //RSA/ECB/OAEPWithSHA-256AndMGF1Padding private static final String RSA_MODE_OAEP = "RSA/ECB/PKCS1Padding"; private static final String KEY_ALIAS = "123"; /** * 创建秘钥 */ public static void createKey() { KeyPairGenerator keyPairGenerator = null; try { keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, AndroidKeyStore); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } AlgorithmParameterSpec spec; Calendar start = Calendar.getInstance(); Calendar end = Calendar.getInstance(); end.add(Calendar.YEAR, 30); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { spec = new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_DECRYPT|KeyProperties.PURPOSE_ENCRYPT) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) .setCertificateNotBefore(start.getTime()) .setCertificateNotAfter(end.getTime()) .build(); } else { spec = new KeyPairGeneratorSpec.Builder(App.getInstance()) .setAlias(KEY_ALIAS) .setSubject(new X500Principal("CN=" + KEY_ALIAS)) .setSerialNumber(BigInteger.TEN) .setStartDate(start.getTime()) .setEndDate(end.getTime()) .build(); } try { keyPairGenerator.initialize(spec); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } keyPairGenerator.generateKeyPair(); } public static String encryptData(String data) { if (!isHaveKeyStore()) { createKey(); } try { Cipher cipher = Cipher.getInstance(RSA_MODE_OAEP); KeyStore keyStore = KeyStore.getInstance(AndroidKeyStore); keyStore.load(null); PublicKey publicKey = keyStore.getCertificate(KEY_ALIAS).getPublicKey(); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return Base64.encodeToString(cipher.doFinal(data.getBytes("UTF-8")), Base64.NO_WRAP); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); return "空指针异常"; } return "加密数据失败"; } public static String decryptData(String data) { try { Cipher cipher = Cipher.getInstance(RSA_MODE_OAEP); KeyStore keyStore = KeyStore.getInstance(AndroidKeyStore); keyStore.load(null); KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS, null); PrivateKey privateKey = privateKeyEntry.getPrivateKey(); cipher.init(Cipher.DECRYPT_MODE, privateKey); return new String(cipher.doFinal(Base64.decode(data, Base64.NO_WRAP)), "UTF-8"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); return "空指针异常"; } catch (UnrecoverableEntryException e) { e.printStackTrace(); } return "解密数据失败"; } /** * 是否创建过秘钥 * * @return */ private static boolean isHaveKeyStore() { try { KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); if (keyStore.containsAlias(KEY_ALIAS)) { return true; } } catch (KeyStoreException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } return false; }}
更多相关文章
- Android中UDP发送报文
- [译]Android架构组件 – 查看ViewModel – 第二部分
- DEX 方法超过64K限制和gradle编译OOM问题解决
- Selector的用法
- android数据持久化总结
- Greendao简单使用
- Android:Xml(读取与存储)
- 开发第四天
- Android搭建客户端连接java构建的服务端