java:AES加密和解密

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。java:AES加密和解密,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

java:AES加密和解密

1 前言

对称加密,即单秘钥加密,指加密和解密的过程中,使用相同的秘钥,相比于非对称加密,因仅有一把钥匙,故而速度更快,更适合解密大文件(常见于如视频文件的加密解密中)。AES算法就属于对称加密中的一种。

2 使用

依赖引入:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.6</version>
</dependency>

<!--  spring-boot 3.12.0  -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

AES加密与解密的工具类封装:

package com.xiaoxu.crawler.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * @author xiaoxu
 * @date 2022-11-06 21:24
 * crawlerJ:com.xiaoxu.crawler.utils.AESUtils
 */
public class AESUtils {

    private static final String AES_ALGORITHM = "AES";

    private static final String UTF8 = StandardCharsets.UTF_8.name();

    /* AES加密 String */
    public static String encryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("encode text should not be null or empty.");
        }
        byte[] encodeBytes = encryptByteAES(text.getBytes(StandardCharsets.UTF_8), key);
        return Base64.encodeBase64String(encodeBytes);
    }

    /* AES解密 String*/
    public static String decryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("decode text should not be null or empty.");
        }
        byte[] decodeBytes = decryptByteAES(Base64.decodeBase64(text.getBytes(StandardCharsets.UTF_8)), key);
        return new String(decodeBytes, StandardCharsets.UTF_8);
    }

    /* AES加密 originalBytes */
    public static byte[] encryptByteAES(byte[] originalBytes, String key){
        if(ArrayUtils.isEmpty(originalBytes)){
            ExcpUtils.throwExp("encode originalBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", encode key should not be null or empty.");
        }
        Cipher cipher = getAESCipher(key, Cipher.ENCRYPT_MODE);
        byte[] encodeBytes = null;
        try {
            encodeBytes = cipher.doFinal(originalBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": encode byte fail. "+e.getMessage());
        }
        return encodeBytes;
    }

    /* AES解密 encryptedBytes */
    public static byte[] decryptByteAES(byte[] encryptedBytes, String key){
        if(ArrayUtils.isEmpty(encryptedBytes)){
            ExcpUtils.throwExp("decode encryptedBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", decode key should not be null or empty.");
        }
        Cipher cipher = getAESCipher(key, Cipher.DECRYPT_MODE);
        byte[] decodeBytes = null;
        try {
            decodeBytes = cipher.doFinal(encryptedBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": decode byte fail. "+e.getMessage());
        }
        return decodeBytes;
    }

    public static Cipher getAESCipher(String key, int mode){
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", should not be null or empty.");
        }
        Cipher cipher = null;
        SecretKey secretKey;
        try {
            cipher = Cipher.getInstance(AES_ALGORITHM);
            byte[] keyBytes = key.getBytes(UTF8);
            secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            cipher.init(mode, secretKey);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": get cipher instance wrong. "+e.getMessage());
        } catch (UnsupportedEncodingException u){
            ExcpUtils.throwExp(u.getClass().getName()+": key transfer bytes fail. "+u.getMessage());
        } catch (InvalidKeyException i) {
            ExcpUtils.throwExp(i.getClass().getName()+": key is invalid. "+i.getMessage());
        }
        return cipher;
    }

    public static void main(String[] args) {
        String msg = "小徐123testAes!!!";
        String key = "0382f2bcbacfa8be";
//        String key = "0382f2bcbacfa8b";
        System.out.println("AES秘钥长度只能为16、24、32:"+key.getBytes(StandardCharsets.UTF_8).length);
        String s = encryptStrAES(msg, key);
        System.out.println("加密后:"+s);
        String s1 = decryptStrAES(s, key);
        System.out.println("解密后:"+s1);
    }


}

执行如下:

AES秘钥长度只能为162432:16
加密后:ZfZYz9Pk4RcA3QQcv+rKnjM54AQV4s/LjsRiGbkpzw8=
解密后:小徐123testAes!!!

AES的秘钥字节数组长度仅能为16、24、32,修改秘钥长度为15再次执行:

public static void main(String[] args) {
    String msg = "小徐123testAes!!!";
//        String key = "0382f2bcbacfa8be";
    String key = "0382f2bcbacfa8b";
    System.out.println("AES秘钥长度只能为16、24、32:"+key.getBytes(StandardCharsets.UTF_8).length);
    String s = encryptStrAES(msg, key);
    System.out.println("加密后:"+s);
    String s1 = decryptStrAES(s, key);
    System.out.println("解密后:"+s1);
}

结果如下,出现了Invalid AES key length异常:

AES秘钥长度只能为162432:15
Exception in thread "main" com.xiaoxu.crawler.excp.CrawlerForJException: java.security.InvalidKeyException: key is invalid. Invalid AES key length: 15 bytes
	at com.xiaoxu.crawler.utils.ExcpUtils.throwExp(ExcpUtils.java:28)
	at com.xiaoxu.crawler.utils.AESUtils.getAESCipher(AESUtils.java:94)
	at com.xiaoxu.crawler.utils.AESUtils.encryptByteAES(AESUtils.java:50)
	at com.xiaoxu.crawler.utils.AESUtils.encryptStrAES(AESUtils.java:29)
	at com.xiaoxu.crawler.utils.AESUtils.main(AESUtils.java:104)

debug源码可见:

AES使用的秘钥key参数有长度限制,如下可见ElectronicCodeBook的部分源码:

在这里插入图片描述

var2为传入的加密的算法,为AES;var3就是秘钥key转换的字节数组,总共长度为15;调用的是AESCrypt的init方法,如下可见AESCrypt的init源码:

void init(boolean var1, String var2, byte[] var3) throws InvalidKeyException {
    if (!var2.equalsIgnoreCase("AES") && !var2.equalsIgnoreCase("Rijndael")) {
        throw new InvalidKeyException("Wrong algorithm: AES or Rijndael required");
    } else if (!isKeySizeValid(var3.length)) {
        throw new InvalidKeyException("Invalid AES key length: " + var3.length + " bytes");
    } else {
        if (!MessageDigest.isEqual(var3, this.lastKey)) {
            this.makeSessionKey(var3);
            this.lastKey = (byte[])var3.clone();
        }

        this.K = this.sessionK[var1 ? 1 : 0];
    }
}

可见,isKeySizeValid(var3.length)返回false时,即抛出Invalid AES key length异常,如下为isKeySizeValid源码:

AESCrypt部分源码如下:

static final boolean isKeySizeValid(int var0) {
    for(int var1 = 0; var1 < AES_KEYSIZES.length; ++var1) {
        if (var0 == AES_KEYSIZES[var1]) {
            return true;
        }
    }

    return false;
}

其中AES_KEYSIZES为接口中的常量:

interface AESConstants {
    int AES_BLOCK_SIZE = 16;
    int[] AES_KEYSIZES = new int[]{16, 24, 32};
}

即AES的秘钥字节数组长度如果不为16、24、32,将抛出Invalid AES key length异常。

由此可见,AES的秘钥字节数组长度需限制在16、24、32内。

package com.xiaoxu.crawler.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author xiaoxu
 * @date 2022-11-06 21:24
 * crawlerJ:com.xiaoxu.crawler.utils.AESUtils
 */
public class AESUtils {

    private static final String AES_ALGORITHM = "AES";

    private static final String UTF8 = StandardCharsets.UTF_8.name();

    private static final List<Integer> AES_KEYSIZES = Arrays.asList(16, 24, 32);

    public static String newKey(String key){
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("new key should not be empty");
        }
        boolean fl = false;
        int len = key.getBytes(StandardCharsets.UTF_8).length;
        for (int keySize : AES_KEYSIZES) {
            if(len == keySize){
                fl = true;
                break;
            }
        }
        String newKey = key;
        if(!fl){
            /* 32、24、16倒序排序 */
            List<Integer> new_sizes = AES_KEYSIZES.stream().sorted(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2-o1;
                }
            }).collect(Collectors.toList());
            int length = 0;
            for (int var0 = 0; var0 < new_sizes.size(); var0++) {
                if(len > 0 && len < new_sizes.get(new_sizes.size()-1) && var0 == new_sizes.size()-1){
                    length = len;
                }else if(new_sizes.get(var0) <= len){
                    length = new_sizes.get(var0);
                }
                if(new_sizes.get(var0) <= len || (len > 0 && len < new_sizes.get(new_sizes.size()-1)&& var0 == new_sizes.size()-1 ) ){
                    byte[] nKey = new byte[new_sizes.get(var0)];
                    for (int f = 0; f < new_sizes.get(var0); f++) {
	                    /* 不足16位的末尾补0 */
                        nKey[f] = (byte)48;
                    }
                    System.arraycopy(key.getBytes(StandardCharsets.UTF_8),0,nKey,0,length);
                    newKey = new String(nKey, StandardCharsets.UTF_8);
                    break;
                }
            }
        }
        return newKey;
    }

    /* AES加密 String */
    public static String encryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("encode text should not be null or empty.");
        }
        byte[] encodeBytes = encryptByteAES(text.getBytes(StandardCharsets.UTF_8), key);
        return Base64.encodeBase64String(encodeBytes);
    }

    /* AES解密 String*/
    public static String decryptStrAES(String text, String key){
        if(!StringUtils.hasLength(text)){
            ExcpUtils.throwExp("decode text should not be null or empty.");
        }
        byte[] decodeBytes = decryptByteAES(Base64.decodeBase64(text.getBytes(StandardCharsets.UTF_8)), key);
        return new String(decodeBytes, StandardCharsets.UTF_8);
    }

    /* AES加密 originalBytes */
    public static byte[] encryptByteAES(byte[] originalBytes, String key){
        if(ArrayUtils.isEmpty(originalBytes)){
            ExcpUtils.throwExp("encode originalBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", encode key should not be null or empty.");
        }

        Cipher cipher = getAESCipher(key, Cipher.ENCRYPT_MODE);
        byte[] encodeBytes = null;
        try {
            encodeBytes = cipher.doFinal(originalBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": encode byte fail. "+e.getMessage());
        }
        return encodeBytes;
    }

    /* AES解密 encryptedBytes */
    public static byte[] decryptByteAES(byte[] encryptedBytes, String key){
        if(ArrayUtils.isEmpty(encryptedBytes)){
            ExcpUtils.throwExp("decode encryptedBytes should not be empty.");
        }
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", decode key should not be null or empty.");
        }
        Cipher cipher = getAESCipher(key, Cipher.DECRYPT_MODE);
        byte[] decodeBytes = null;
        try {
            decodeBytes = cipher.doFinal(encryptedBytes);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": decode byte fail. "+e.getMessage());
        }
        return decodeBytes;
    }

    public static Cipher getAESCipher(String key, int mode){
        if(!StringUtils.hasLength(key)){
            ExcpUtils.throwExp("key :" + key + ", should not be null or empty.");
        }
        Cipher cipher = null;
        SecretKey secretKey;
        try {
            cipher = Cipher.getInstance(AES_ALGORITHM);
            byte[] keyBytes = key.getBytes(UTF8);
            secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            cipher.init(mode, secretKey);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            ExcpUtils.throwExp(e.getClass().getName()+": get cipher instance wrong. "+e.getMessage());
        } catch (UnsupportedEncodingException u){
            ExcpUtils.throwExp(u.getClass().getName()+": key transfer bytes fail. "+u.getMessage());
        } catch (InvalidKeyException i) {
            ExcpUtils.throwExp(i.getClass().getName()+": key is invalid. "+i.getMessage());
        }
        return cipher;
    }

    public static void main(String[] args) {
        String msg = "小徐123testAes!!!";
//        String key = "0382f2bcbacfa8be";
        String key = "0382f2bcbacfa8b";
        System.out.println("转换前的秘钥长度:" + key.getBytes(StandardCharsets.UTF_8).length);
        System.out.println("转换后秘钥:"+newKey(key)+";长度:"+newKey(key).getBytes(StandardCharsets.UTF_8).length);

        System.out.println("AES秘钥长度只能为16、24、32:"+newKey(key).getBytes(StandardCharsets.UTF_8).length);
        String s = encryptStrAES(msg, newKey(key));
        System.out.println("加密后:"+s);
        String s1 = decryptStrAES(s, newKey(key));
        System.out.println("解密后:"+s1);
    }
}

可如上简单对秘钥长度作出填充,执行结果如下:

转换前的秘钥长度:15
转换后秘钥:0382f2bcbacfa8b0;长度:16
AES秘钥长度只能为162432:16
加密后:AAz4gPXlduN+l1OX0BV9nWXqJqhXRS3ThRQXJsU0lWM=
解密后:小徐123testAes!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/192105.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!