场景

调用第三方接口返回数据需要AES/ECB/PKCS5Padding解密。

导入的包

import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
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.util.Base64;

代码片段

public class AESUtil {
    private static Logger log = LoggerFactory.getLogger(AESUtil.class);
    /**
     * 密钥算法
     */
    private static final String ALGORITHM = "AES";
    /**
     * 加解密算法/工作模式/填充方式
     */
    private static final String ALGORITHM_STR = "AES/ECB/PKCS5Padding";
    /**
     * SecretKeySpec类是KeySpec接口的实现类,用于构建秘密密钥规范
     */
    private SecretKeySpec key;
    public AESUtil(String hexKey) {
        key = new SecretKeySpec(hexKey.getBytes(), ALGORITHM);
    }
    /**
     * AES解密
     * @param base64Data
     * @return
     * @throws Exception
     */
    public String decryptData(String base64Data) throws Exception{
        Cipher cipher = Cipher.getInstance(ALGORITHM_STR);
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(base64Data)));
    }

	/**
	 * 获取密钥
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String initKey() throws Exception {  
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        //192,256
        keyGenerator.init(128);
        SecretKey secretKey = keyGenerator.generateKey();  
        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }  
	
	/** 
     * AES+BASE64加密
     * 加密方法
     * 与 解密方法 {@link #decrypt} 配套使用
     * @param data 要加密的数据 
     * @param key  加密所使用的密钥 
     * @return 加密后的数据 
     * @throws Exception 
     */  
    public static String encrypt(String data, String key) throws Exception {  
    	byte[] keyBytes = Base64.getDecoder().decode(key);
        SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");  
        Cipher decryptionCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
        byte[] iv = KeyGenerator.getInstance("AES").generateKey().getEncoded();
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        decryptionCipher.init(Cipher.ENCRYPT_MODE, secretKey , ivSpec );
        byte[] cipherText = decryptionCipher.doFinal(data.getBytes());
        byte[] iv_cipher = new byte[iv.length + cipherText.length];
        int i = 0 ;
        for (; i < iv.length; i++) {
        	iv_cipher[i] = iv[i];
        }
        for (int j = 0; j < cipherText.length; j++) {
        	iv_cipher[(i + j)] = cipherText[j];
        }
        return Base64.getEncoder().encodeToString(iv_cipher);  
    }  
    
    /** 
     * AES+BASE64解密 
     * 与 加密方法{@link #encrypt}  配套使用
     * @param data 要解密的数据 
     * @param key  解密所使用的密钥 
     * @return 解密后的数据, 即源数据 
     * @throws Exception 
     */  
    public static String decrypt(String data, String key) throws Exception {
    	byte[] keyBytes = Base64.getDecoder().decode(key);
        SecretKey keySpec = new SecretKeySpec(keyBytes, "AES");
        Cipher decryptionCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv_cipher = Base64.getDecoder().decode(data);
        byte[] iv = new byte[16];
        byte[] cipher = new byte[iv_cipher.length - 16];
        int i = 0;
        int j = 0;
        for (; i < 16; i++) {
        	iv[i] = iv_cipher[i];
        }
        for (;  i < iv_cipher.length; i++) {
        	cipher[j] = iv_cipher[i];
        	j++;
        }
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        decryptionCipher.init(2, keySpec, ivSpec);
        return new String(decryptionCipher.doFinal(cipher), "UTF-8");
    }


    /**
     * 加密方法
     * 与 解密方法{@link #decryptWithHex} 配套使用
     * 将不符合要求的密钥处理为16位的ASCII编码的数组,使用AES加密,
     * 将加密的字节数组转为十六进制字符数组,然后将其转换为utf-8字符串。
     * @param preString 待加密的字符串
     * @param strKey 密钥
     * @return
     */
    public static String encryptWithHex(String preString, String strKey) {
        try {
            SecretKey key = generateMySQLAESKey(strKey,"ASCII");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] cleartext = preString.getBytes("UTF-8");
            byte[] ciphertextBytes = cipher.doFinal(cleartext);
            return new String(Hex.encodeHex(ciphertextBytes));

        } catch (Exception e) {
            log.error("{}字符串通过AES密钥{}加密异常",preString, strKey, e);
        }
        return null;
    }

    /**
     * 解密方法
     * 与 加密方法{@link #encryptWithHex}  配套使用
     * @param preString 待加密的字符串
     * @param strKey 密钥
     */
    public static String decryptWithHex(String preString, String strKey){
        try {
            SecretKey key = generateMySQLAESKey(strKey,"ASCII");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] cleartext = Hex.decodeHex(preString.toCharArray());
            byte[] ciphertextBytes = cipher.doFinal(cleartext);
            return new String(ciphertextBytes, "UTF-8");

        } catch (Exception e) {
            log.error("{}字符串通过AES密钥{}解密异常",preString, strKey, e);
        }
        return null;
    }


    /**
     * 将不符合要求的密钥处理为16位的ASCII编码的字节数组
     * 数据库AES算法也是如此处理的
     */
    private static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) {
        try {
            final byte[] finalKey = new byte[16];
            int i = 0;
            for(byte b : key.getBytes(encoding)){
                finalKey[i++%16] ^= b;
            }
            return new SecretKeySpec(finalKey, "AES");
        } catch(UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
}

总结

如有其他更好的方法或者想法,可以留言或联系分享,大家互相学习进步。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐