Java 实现 AES 对称加密算法的加解密

  • 前言
  • 一、对称加密算法简介
  • 1.对称加密
  • 2.加密模式
  • 3.填充模式
  • 二、AES 加解密代码实例
  • 1.生成 AES 密钥
  • 2.AES 加解密
  • 3.AES + nonce 加解密


前言

文章字数比较多,可直接查看代码:源码地址,文中描述有误的地方欢迎各位大神指导。

一、对称加密算法简介

1.对称加密

采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

对称加密特点:

  • 加密速度快,可以加密大文件。
  • 密文可逆,一旦密钥文件泄漏,就会导致数据暴露。
  • 加密后编码表找不到对应字符会出现乱码。
  • 一般结合Base64使用。

2.加密模式

ECB

ECB(Electronic codebook):电子密码本,需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密。

  • 优点 : 可以并行处理数据。
  • 缺点 : 同样的原文生成同样的密文,不能很好的保护数据。

CBC

CBC(Cipher-block chaining):密码块链接,每个明文块先与前一个密文块进行异或,然后再进行加密,在这种方法中,每个密文块都依赖于它前面的所有明文块。

  • 优点 : 同样的原文生成的密文不一样。
  • 缺点 : 串行处理数据。

3.填充模式

当需要按块处理的数据,数据长度不符合块处理需求时,按照一定的方法填充满块长的规则。

NoPadding

  • 不填充,在AES加密算法下,要求原文长度必须是16byte的整数倍。

PKCS5Padding

  • 数据块的大小为8位不够就补足。

二、AES 加解密代码实例

1.生成 AES 密钥

  • AES 密钥长度默认只支持128、192、256 这三种长度,不合法的密钥长度程序会抛出异常。
  • 生成 AES 密钥时会使用到随机数生成器,可以指定不同的随机数算法,也可以在创建随机数生成器时指定 seed。
  • 初始化算法生成器时,如果不指定随机数生成器默认使用 new SecureRandom()。

代码如下:

public static SecretKey generateAESKey(int keysize) {
        try {
            // 校验密钥长度
            if (keysize != AES_KEY_SIZE_128 && keysize != AES_KEY_SIZE_192 && keysize != AES_KEY_SIZE_256) {
                keysize = AES_KEY_SIZE_128;
            }
            // 创建安全随机数生成器(SHA1PRNG)
            SecureRandom random = SecureRandom.getInstance(RNG_ALGORITHM);
            // 创建 AES 算法生成器(AES)
            KeyGenerator generator = KeyGenerator.getInstance(AES_ALGORITHM);
            // 初始化算法生成器
            generator.init(keysize, random);
            return generator.generateKey();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

2.AES 加解密

TODO

代码如下:

/**
     * AES 加密
     *
     * @param aseKey 密钥
     * @param plain  加密原文
     * @return 密文
     */
    public static byte[] aesEncrypt(byte[] aseKey, byte[] plain) {
        try {
            SecretKey secretKey = new SecretKeySpec(aseKey, AES_ALGORITHM);
            // AES
            Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return cipher.doFinal(plain);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * AES 解密
     *
     * @param aseKey    AES 密钥
     * @param encrypted 解密密文
     * @return 原文
     */
    public static byte[] aesDecrypt(byte[] aseKey, byte[] encrypted) {
        try {
            SecretKey secretKey = new SecretKeySpec(aseKey, AES_ALGORITHM);
            // AES
            Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return cipher.doFinal(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

3.AES + nonce 加解密

注意在使用 nonce 时,nonce 的长度不能超过 128,不然程序会抛出异常。

代码如下:

/**
     * AES 加密
     *
     * @param aseKey AES 密钥
     * @param plain  加密原文
     * @param nonce  随机值
     * @return 密文
     */
    public static byte[] aesEncrypt(byte[] aseKey, byte[] plain, byte[] nonce) {
        try {
            // AES
            SecretKey secretKeySpec = new SecretKeySpec(aseKey, AES_ALGORITHM);
            // 获取 AES 密码器(AES/GCM/PKCS5Padding)
            Cipher cipher = Cipher.getInstance(AES_GCM_ALGORITHM);
            GCMParameterSpec zeroIv = new GCMParameterSpec(128, nonce);
            // 初始化密码器(加密模型)
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, zeroIv);
            return cipher.doFinal(plain);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
        /**
     * AES 解密
     *
     * @param aseKey    AES 密钥
     * @param encrypted 解密密文
     * @param nonce     随机值
     * @return 原文
     */
    public static byte[] aesDecrypt(byte[] aseKey, byte[] encrypted, byte[] nonce) {
        try {
			// AES
            SecretKeySpec secretKeySpec = new SecretKeySpec(aseKey, AES_ALGORITHM);
			// 获取 AES 密码器(AES/GCM/PKCS5Padding)
            Cipher cipher = Cipher.getInstance(AES_GCM_ALGORITHM);
            GCMParameterSpec zeroIv = new GCMParameterSpec(128, nonce);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, zeroIv);
            return cipher.doFinal(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 获取随机值
     *
     * @param len 随机值长度
     * @return
     */
    public static byte[] generatorNonce(int len) {
        byte[] values = new byte[len];
        SecureRandom random = new SecureRandom();
        random.nextBytes(values);
        return values;
    }