AES对称加密 Java实现

发布于:2025-09-09 ⋅ 阅读:(22) ⋅ 点赞:(0)

AES(Advanced Encryption Standard)是目前广泛使用的一种对称加密算法。它的加密速度快、强度高,被广泛应用于文件加密、传输加密等领域。

一、AES 基本概念

AES 是对称加密算法,这意味着加密和解密使用同一个密钥。AES 支持三种密钥长度:128 位192 位 和 256 位。AES 是基于 分组加密(Block Cipher) 的算法,每次加密固定长度的明文块(通常是 128 位,即 16 字节),如果明文不足 16 字节,需要进行填充(Padding)

二、分组模式和填充方式

1. 分组模式(Block Cipher Mode)

AES 是分组加密算法,每次只能加密一个固定长度的块,为了加密长度不定的明文,需要采用不同的分组模式。常见的分组模式包括:

  • ECB(Electronic Codebook)模式:每个明文块独立加密。这种方式简单,但是安全性较低,因为相同的明文块会得到相同的密文块,容易被攻击。
  • CBC(Cipher Block Chaining)模式:每个明文块在加密前先与前一个密文块进行异或操作。需要一个初始向量(IV)来确保每次加密得到不同的密文。
  • CFB(Cipher Feedback)模式:将前一密文块作为输入生成伪随机数流,然后与明文块异或得到密文块。
  • OFB(Output Feedback)模式:与 CFB 类似,但它将上一次的输出反馈作为下一次加密输入,而不是使用密文块。
  • CTR(Counter)模式:将一个计数器的输出作为输入来生成伪随机数流,与明文块异或,属于流模式加密。

其中,CBC 模式CTR 模式比较常用,因为它们提供了更好的安全性。

2. 填充方式(Padding)

AES 的分组长度是固定的 128 位(16 字节),但实际明文长度往往不是 16 字节的倍数,因此需要填充。常见的填充方式有:

  • PKCS5Padding:最常用的填充方式,填充的每个字节的值为剩余填充的字节数。
  • PKCS7Padding:与 PKCS5Padding 类似,但可以用于大于 8 字节分组的加密算法。
  • NoPadding:不进行填充,但要求输入的明文长度必须是分组大小的整数倍。

三、Java代码实现


import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * AES加密解密工具类(目前AES比DES和DES3更安全,速度更快,对称加密一般采用AES)
 */
public class AESUtil {

    private static final String AES = "AES";

    /**
     * 格式为"{加密算法}/{模式}/{填充方式}" 。
     * (1) 加密算法:AES
     * (2) 模式: ECB(无向量模式), CBC(有向量模式), CFB, OFB, CTR(流模式),通常CBC和CTR比较常用, 因为他们提供了更好的安全性
     * (3) 填充方式: NoPadding(适用于流模式CTR)、PKCS5Padding(常见)、PKCS7Padding
     */
    private static final String AES_ECB_PKCS5PADDING = "AES/ECB/PKCS5Padding";

    private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5Padding";

    private static final String AES_CTR_NOPADDING = "AES/CTR/NoPadding";

    /**
     * 生成AES密钥
     * @param keySize, 可选128, 192, 256
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static String generateKey(int keySize) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
        keyGenerator.init(keySize, new SecureRandom());
        SecretKey secretKey = keyGenerator.generateKey();
        return new String(Base64.getEncoder().encode(secretKey.getEncoded()), "UTF-8");
    }

    public static String generateIv() throws UnsupportedEncodingException {
        // IV长度比须为16
        byte[] ivBytes = new byte[16];
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.nextBytes(ivBytes);
        String iv = new String(Base64.getEncoder().encode(ivBytes), "UTF-8");
        return iv;
    }


    public static String encryptECB(String data, String key) throws Exception {
        //根据参数获取加密实例
        Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5PADDING);
        //初始化对象为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKeySpec(key));
        //加密后的密文进行base64编码
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
    }

    /**
     * 使用AES加密数据
     *
     * @param data 加密数据
     * @param key  密钥
     * @param iv   初始化随机向量
     * @return
     * @throws Exception
     */
    public static String encryptCBC(String data, String key, String iv) throws Exception {
        //根据参数获取加密实例
        Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
        //初始化对象为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKeySpec(key), getIv(iv));
        //加密后的密文进行base64编码
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
    }

    public static String encryptCTR(String data, String key, String iv) throws Exception {
        //根据参数获取加密实例
        Cipher cipher = Cipher.getInstance(AES_CTR_NOPADDING);
        //初始化对象为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKeySpec(key), getIv(iv));
        //加密后的密文进行base64编码
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
    }



    private static IvParameterSpec getIv(String iv) {
        byte[] ivBytes = Base64.getDecoder().decode(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        return ivSpec;
    }



    private static SecretKeySpec getSecretKeySpec(String key) {
        //密钥base64解码
        byte[] decode = Base64.getDecoder().decode(key);
        SecretKeySpec secretKeySpec = new SecretKeySpec(decode, AES);
        return secretKeySpec;
    }

    /**
     * 使用AES解密密文
     *
     * @param encrypt 加密后的密文
     * @param key     密钥
     * @return
     * @throws Exception
     */
    public static String decryptECB(String encrypt, String key) throws Exception {
        Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5PADDING);
        //初始化对象为解密模式
        cipher.init(Cipher.DECRYPT_MODE, getSecretKeySpec(key));
        byte[] dateBytes = Base64.getDecoder().decode(encrypt);
        //解密并转换为字符串
        return new String(cipher.doFinal(dateBytes));
    }

    public static String decryptCBC(String encrypt, String key, String iv) throws Exception {
        Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
        //初始化对象为解密模式
        cipher.init(Cipher.DECRYPT_MODE, getSecretKeySpec(key), getIv(iv));
        byte[] dateBytes = Base64.getDecoder().decode(encrypt);
        //解密并转换为字符串
        return new String(cipher.doFinal(dateBytes));
    }

    public static String decryptCTR(String encrypt, String key, String iv) throws Exception {
        Cipher cipher = Cipher.getInstance(AES_CTR_NOPADDING);
        //初始化对象为解密模式
        cipher.init(Cipher.DECRYPT_MODE, getSecretKeySpec(key), getIv(iv));
        byte[] dateBytes = Base64.getDecoder().decode(encrypt);
        //解密并转换为字符串
        return new String(cipher.doFinal(dateBytes));
    }

    public static void main(String[] args) throws Exception {
        String key = AESUtil.generateKey(256);
        System.out.println("key = " + key);

        String iv = generateIv();
        System.out.println("iv:" + iv);

        String plainText = "Hello,world!";
        System.out.println("原始数据:" + plainText);

        String encryptECB = AESUtil.encryptECB(plainText, key);
        System.out.println("ECB加密:" + encryptECB);
        String decryptECB = AESUtil.decryptECB(encryptECB, key);
        System.out.println("ECB解密:" + decryptECB);


        String encryptCBC = AESUtil.encryptCBC(plainText, key, iv);
        System.out.println("CBC加密:" + encryptCBC);
        String decryptCBC = AESUtil.decryptCBC(encryptCBC, key, iv);
        System.out.println("CBC解密:" + decryptCBC);


        String encryptCTR = AESUtil.encryptCTR(plainText, key, iv);
        System.out.println("CTR加密:" + encryptCTR);
        String decryptCTR = AESUtil.decryptCTR(encryptCTR, key, iv);
        System.out.println("CTR解密:" + decryptCTR);
    }

}


网站公告

今日签到

点亮在社区的每一天
去签到