AES对称加密

AES对称加密,比DES安全性高。

AES通常用于移动通信系统加密以及基于SSH协议的软件。

AES对称加密:AES常用的有ECB和CBC两种模式。常用的填充方式有PKCS5Padding、PKCS7Padding、zeropadding
CBC模式比ECB模式安全,ECB模式比CBC模式快。 推荐使用CBC模式。
相同密码的加密结果不会变化,是固定的。
这个标准用来替代原先的DES

key的字节长度只能是16位、24位、32位。
iv的字节长度只能是16位。
KEY和IV可以相同,也可以不相同。
key和iv长度不足时,这里将以0x00自动填充,超出部分将被忽略。注意:其他系统不一定是这种逻辑,所以key建议设置为16/24/32位,iv设置为16位。

说明:AES数据块长度为128位(16字节),所以IV长度需要为16个字节(ECB模式不用IV),IV与KEY超过长度则截取,不足则在末尾填充'\0'补足

package com.study;


import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * AES对称加密:AES常用的有ECB和CBC两种模式。常用的填充方式有PKCS5Padding、PKCS7Padding、zeropadding
 * CBC模式比ECB模式安全,ECB模式比CBC模式快。 推荐使用CBC模式。
 * 相同密码的加密结果不会变化,是固定的。
 * 这个标准用来替代原先的DES
 * <p>
 * key的字节长度只能是16位、24位、32位。
 * iv的字节长度只能是16位。
 * KEY和IV可以相同,也可以不相同。
 * key和iv长度不足时,这里将以0x00自动填充,超出部分将被忽略。注意:其他系统不一定是这种逻辑,所以key建议设置为16/24/32位,iv设置为16位。
 * <p>
 * 说明:AES数据块长度为128位(16字节),所以IV长度需要为16个字节(ECB模式不用IV),IV与KEY超过长度则截取,不足则在末尾填充'\0'补足
 * @date 2019年12月23日 下午7:56:33
 */
public class AESUtil {
    private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);
    private static final String CBCMode = "AES/CBC/PKCS5Padding";//填充方式
    private static final String ECBMode = "AES/ECB/PKCS5Padding";//填充方式

    public static void main(String[] args) {
        String key = "1234567812345678";// key长度建议设置为16位或者24位或者32位。
        String iv = "1234567887654321";// iv长度建议设置为16位。
        String msg = "测试字符串";

        //CBC模式
        String cbcEncrypt = AESUtil.encryptCBC(msg, key, iv);
        System.out.println(cbcEncrypt);
        String cbcDecryptStr = AESUtil.decryptCBC(cbcEncrypt, key, iv);
        System.out.println(cbcDecryptStr);

        //ECB模式
        String ecbEncrypt = AESUtil.encryptECB(msg, key);
        System.out.println(ecbEncrypt);
        String decryptStr = AESUtil.decryptECB(ecbEncrypt, key);
        System.out.println(decryptStr);
    }

    /**
     * AES CBC 加密
     * @param message 需要加密的字符串
     * @param key     密匙
     * @param iv      IV,需要和key长度相同
     * @return 返回加密后密文,编码为base64
     */
    public static String encryptCBC(String message, String key, String iv) {
        try {
            byte[] content = message.getBytes(StandardCharsets.UTF_8);
            byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
            keyByte = fillKey(keyByte);
            SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
            byte[] ivByte = iv.getBytes(StandardCharsets.UTF_8);
            ivByte = fillIv(ivByte);
            IvParameterSpec ivSpec = new IvParameterSpec(ivByte);
            Cipher cipher = Cipher.getInstance(CBCMode);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            byte[] data = cipher.doFinal(content);
            return Base64.getEncoder().encodeToString(data);
        } catch (NoSuchAlgorithmException e) {
            logger.error("没有这样的算法", e);
        } catch (NoSuchPaddingException e) {
            logger.error("没有这样的填充", e);
        } catch (InvalidKeyException e) {
            logger.error("无效的密钥", e);
        } catch (InvalidAlgorithmParameterException e) {
            logger.error("无效的算法参数", e);
        } catch (IllegalBlockSizeException e) {
            logger.error("非法块大小", e);
        } catch (BadPaddingException e) {
            logger.error("不良填充", e);
        }
        return null;
    }

    /**
     * AES CBC 解密
     * @param messageBase64 密文,base64编码
     * @param key           密匙,和加密时相同
     * @param iv            IV,需要和key长度相同
     * @return 解密后数据
     */
    public static String decryptCBC(String messageBase64, String key, String iv) {
        try {
            byte[] messageByte = Base64.getDecoder().decode(messageBase64);
            byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
            keyByte = fillKey(keyByte);
            SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
            byte[] ivByte = iv.getBytes(StandardCharsets.UTF_8);
            ivByte = fillIv(ivByte);
            IvParameterSpec ivSpec = new IvParameterSpec(ivByte);
            Cipher cipher = Cipher.getInstance(CBCMode);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            byte[] content = cipher.doFinal(messageByte);
            return new String(content, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException e) {
            logger.error("没有这样的算法", e);
        } catch (NoSuchPaddingException e) {
            logger.error("没有这样的填充", e);
        } catch (InvalidKeyException e) {
            logger.error("无效的密钥", e);
        } catch (InvalidAlgorithmParameterException e) {
            logger.error("无效的算法参数", e);
        } catch (IllegalBlockSizeException e) {
            logger.error("非法块大小", e);
        } catch (BadPaddingException e) {
            logger.error("不良填充", e);
        }
        return null;
    }

    /**
     * AES ECB 加密
     * @param message 需要加密的字符串
     * @param key     密匙
     * @return 返回加密后密文,编码为base64
     */
    public static String encryptECB(String message, String key) {
        try {
            byte[] content = message.getBytes(StandardCharsets.UTF_8);
            byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
            keyByte = fillKey(keyByte);
            SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
            Cipher cipher = Cipher.getInstance(ECBMode);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            byte[] data = cipher.doFinal(content);
            return Base64.getEncoder().encodeToString(data);
        } catch (NoSuchAlgorithmException e) {
            logger.error("没有这样的算法", e);
        } catch (NoSuchPaddingException e) {
            logger.error("没有这样的填充", e);
        } catch (InvalidKeyException e) {
            logger.error("无效的密钥", e);
        } catch (IllegalBlockSizeException e) {
            logger.error("非法块大小", e);
        } catch (BadPaddingException e) {
            logger.error("不良填充", e);
        }
        return null;
    }

    /**
     * AES ECB 解密
     * @param messageBase64 密文,base64编码
     * @param key           密匙,和加密时相同
     * @return 解密后数据
     */
    public static String decryptECB(String messageBase64, String key) {
        try {
            byte[] messageByte = Base64.getDecoder().decode(messageBase64);
            byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
            keyByte = fillKey(keyByte);
            SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
            Cipher cipher = Cipher.getInstance(ECBMode);
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            byte[] content = cipher.doFinal(messageByte);
            return new String(content, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException e) {
            logger.error("没有这样的算法", e);
        } catch (NoSuchPaddingException e) {
            logger.error("没有这样的填充", e);
        } catch (InvalidKeyException e) {
            logger.error("无效的密钥", e);
        } catch (IllegalBlockSizeException e) {
            logger.error("非法块大小", e);
        } catch (BadPaddingException e) {
            logger.error("不良填充", e);
        }
        return null;
    }


    /**
     * 填充key
     * key的字节长度只能是16位、24位、32位。
     * key长度不足时,这里将以0x00填充,超出部分将被忽略。
     * @return 填充后的key
     */
    public static byte[] fillKey(byte[] keyByte) {
        int length = keyByte.length;
        int len;
        if (length == 16 || length == 24 || length == 32) {
            return keyByte;
        } else if (length < 16) {
            len = 16;
        } else if (length < 24) {
            len = 24;
        } else {
            len = 32;
        }
        byte[] newKeyByte = new byte[len];
        System.arraycopy(keyByte, 0, newKeyByte, 0, length < len ? length : len);
        return newKeyByte;
    }

    /**
     * 填充iv
     * iv的字节长度只能是16位。
     * iv长度不足时,这里将以0x00填充,超出部分将被忽略。
     * @return 填充后的iv
     */
    public static byte[] fillIv(byte[] ivByte) {
        int length = ivByte.length;
        int len;
        if (length == 16) {
            return ivByte;
        } else {
            len = 16;
        }
        byte[] newIvByte = new byte[len];
        System.arraycopy(ivByte, 0, newIvByte, 0, length < len ? length : len);
        return newIvByte;
    }
}

AES GCM 模式

package com.study;


import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;

public class AESUtil {
    private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);
    private static String gcm256algorithm = "AES/GCM/PKCS5Padding";

    public static void main(String[] args) {
        KeyGenerator generator = null;
        try {
            generator = KeyGenerator.getInstance("AES");
            //初始化密钥生成器,AES要求密钥长度为128位、192位、256位
            generator.init(256);
            SecretKey secretKey = generator.generateKey();
            // String gcmSecretKey = Base64.encodeBase64String(secretKey.getEncoded());
            String key = "1234567812345678";//key长度为16的倍数
            String gcmSecretKey = Base64.encodeBase64String(key.getBytes(StandardCharsets.UTF_8));
            System.out.println("生成密钥key: " + key + "\n");

            String toDoStr = "12345678";
            System.out.println("明文文本:" + toDoStr);
            String encryptResult = AEGCMEncrypt(toDoStr, gcmSecretKey);
            System.out.println("加密结果: " + encryptResult);

            String decryptResult = AEGCMDecrypt(encryptResult, gcmSecretKey);
            System.out.println("解密结果: " + decryptResult + "\n");


        } catch (NoSuchAlgorithmException e) {
            logger.error("密钥生成异常,error:", e);
        }
    }

    //解密
    public static String AEGCMDecrypt(String content, String keyStr) {
        try {
            if (StringUtils.isEmpty(content) || StringUtils.isEmpty(keyStr)) {
                throw new Exception("AESGCM256解密异常,检查文本或密钥");
            }
            Cipher cipher = Cipher.getInstance(gcm256algorithm);
            SecretKey key = new SecretKeySpec(Base64.decodeBase64(keyStr), "AES");

            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] message = Base64.decodeBase64(content);
            // 这里的12和16是加密的时候设置的偏移参数及加密长度
            if (message.length < 12 + 16) throw new IllegalArgumentException();
            GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);
            cipher.init(Cipher.DECRYPT_MODE, key, params);
            byte[] decryptData = cipher.doFinal(message, 12, message.length - 12);
            String decript = new String(decryptData);
            return decript;
        } catch (Exception e) {
            logger.error("AESGCM256解密文本处理失败,error:{}", e);
        }
        return null;
    }

    //加密
    public static String AEGCMEncrypt(String content, String keyStr) {
        try {
            if (StringUtils.isEmpty(content) || StringUtils.isEmpty(keyStr)) {
                throw new Exception("AESGCM256加密异常,检查文本或密钥");
            }

            SecretKey secretKey = new SecretKeySpec(Base64.decodeBase64(keyStr), "AES");

            Cipher cipher = Cipher.getInstance(gcm256algorithm);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] iv = cipher.getIV();
            assert iv.length == 12;// 偏移参数及长度要在解密的时候保持一致
            byte[] encryptData = cipher.doFinal(content.getBytes());
            assert encryptData.length == content.getBytes().length + 16;
            byte[] message = new byte[12 + content.getBytes().length + 16];
            System.arraycopy(iv, 0, message, 0, 12);
            System.arraycopy(encryptData, 0, message, 12, encryptData.length);
            return Base64.encodeBase64String(message);
        } catch (Exception e) {
            logger.error("AESGCM256加密文本处理失败,error:{}", e);
        }
        return null;

    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值