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

没有理解为什么啊。请路过的大佬指点。。

/**
 * 对称加密数据
 * <p>
 * 使用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中
 * 对数据进行加解密
 * <p>
 * 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;
    }


}

猜你喜欢

转载自blog.csdn.net/hpp_1225/article/details/83786134
今日推荐