java实现多种加密模式的AES算法-总有一种你用的着

    对于AES算法,相信很多程序员小伙伴都听过、用过,其原理本文就不介绍了,而是讲讲在实际项目中的应用。前几天,项目需要跟乙方接口对接,乙方提供加密后的二维码信息串,而我这边负责对加密串进行解密。其中加解密算法用的就是AES 128位 无向量,加密模式为ECB,填充模式为PKCS7Padding,密钥长度32位。   

    开始的开始,是先参考网上提供的CBC\PKCS7Padding加密模式进行改造,发现其实没有自己想象的辣么简单,因为会直接报如下图足够折腾自己一番的错误,网上了解了一下,发现jdk自带的只是支PKCS5Padding,不支持PKCS7Padding。

    最后的最后,google了一番:发现在获取加密instance前,需要额外添加 BouncyCastleProvider() 这样的provider。具体缘由就没去深究了,毕竟先把问题解决了才是头等大事,其他的先晾在一边!

    接下来是源码实现

    1、AES-128位-无向量-ECB/PKCS7Padding

package com.debug.steadyjack.springbootMQ.server.util;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;

/**
 * AES加密算法util
 * Created by steadyjack on 2018/4/21.
 */
public class AESUtil {

    private static final String EncryptAlg ="AES";

    private static final String Cipher_Mode="AES/ECB/PKCS7Padding";

    private static final String Encode="UTF-8";

    private static final int Secret_Key_Size=32;

    private static final String Key_Encode="UTF-8";

    /**
     * AES/ECB/PKCS7Padding 加密
     * @param content
     * @param key 密钥
     * @return aes加密后 转base64
     * @throws Exception
     */
    public static String aesPKCS7PaddingEncrypt(String content, String key) throws Exception {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            Cipher cipher = Cipher.getInstance(Cipher_Mode);
            byte[] realKey=getSecretKey(key);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(realKey,EncryptAlg));
            byte[] data=cipher.doFinal(content.getBytes(Encode));
            String result=new Base64().encodeToString(data);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("AES加密失败:content=" +content +" key="+key);
        }
    }

    /**
     * AES/ECB/PKCS7Padding 解密
     * @param content
     * @param key 密钥
     * @return 先转base64 再解密
     * @throws Exception
     */
    public static String aesPKCS7PaddingDecrypt(String content, String key) throws Exception {
        try {
            //Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            byte[] decodeBytes=Base64.decodeBase64(content);

            Cipher cipher = Cipher.getInstance(Cipher_Mode);
            byte[] realKey=getSecretKey(key);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(realKey,EncryptAlg));
            byte[] realBytes=cipher.doFinal(decodeBytes);

            return new String(realBytes, Encode);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("AES解密失败:Aescontent = " +e.fillInStackTrace(),e);
        }
    }

    /**
     * 对密钥key进行处理:如密钥长度不够位数的则 以指定paddingChar 进行填充;
     * 此处用空格字符填充,也可以 0 填充,具体可根据实际项目需求做变更
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] getSecretKey(String key) throws Exception{
        final byte paddingChar=' ';

        byte[] realKey = new byte[Secret_Key_Size];
        byte[] byteKey = key.getBytes(Key_Encode);
        for (int i =0;i<realKey.length;i++){
            if (i<byteKey.length){
                realKey[i] = byteKey[i];
            }else {
                realKey[i] = paddingChar;
            }
        }

        return realKey;
    }
}

    下面进行测试:对一串序列化后的json格式字符串进行加密,密钥设定为 debug,测试代码如下:

public static void main(String[] args) throws Exception{
        //密钥 加密内容(对象序列化后的内容-json格式字符串)
        String key="debug";
        String content="{\"domain\":{\"method\":\"getDetails\",\"url\":\"http://www.baidu.com\"},\"name\":\"steadyjack_age\",\"age\":\"23\",\"address\":\"Canada\",\"id\":\"12\",\"phone\":\"15627284601\"}";

        String encryptRes=aesPKCS7PaddingEncrypt(content,key);
        System.out.println(String.format("加密结果:%s ",encryptRes));

        String decryptRes=aesPKCS7PaddingDecrypt(encryptRes,key);
        System.out.println(String.format("解密结果:%s ",decryptRes));
    }

    结果如下

加密结果:mbHFRIwY+aKSFY8NMm3StyekTwmxOUu4YIYOybz6I1qeXofPnYyTEkJr2wjHBFOGdPlJpGq2BjbBN5Dakq27qj+xoyIxKzO52huWzQf8UFCaslKEZOZ3Ub3nuJAepFUzU3JvvoSp/Ei7FHKTgwgWFOs9Oq+A1deOWHyb8fqnwoQTp2zBwzaSdLU4MFf9V2MpTT6H3tP6gf97snSNfQW7VA== 
解密结果:{"domain":{"method":"getDetails","url":"http://www.baidu.com"},"name":"steadyjack_age","age":"23","address":"Canada","id":"12","phone":"15627284601"} 

    附注:
(1)如果想改为 PKCS5Padding 填充模式也是可以的,只需要修改上面工具类的静态常量Cipher_Mode的取值即可,如下所示:

private static final String Cipher_Mode="AES/ECB/PKCS5Padding";

 

(2)上面如果想改为 有向量模式 可以继续参考 下面的文章 进行调整即可

 

 

 2、AES-128位-有向量-CBC/PKCS5Padding    

package com.debug.steadyjack.springbootMQ.server.util;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;

/**
 * AES加解密工具
 * Created by steadyjack on 2018/2/9.
 */
public class EncryptUtil {

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

    private static final String SecretKey="debug";

    private static final Integer IVSize=16;

    private static final String EncryptAlg ="AES";

    private static final String Encode="UTF-8";

    private static final int SecretKeySize=32;

    private static final String Key_Encode="UTF-8";

    /**
     * 创建密钥
     * @return
     */
    private static SecretKeySpec createKey(){
        StringBuilder sb=new StringBuilder(SecretKeySize);
        sb.append(SecretKey);
        if (sb.length()>SecretKeySize){
            sb.setLength(SecretKeySize);
        }
        if (sb.length()<SecretKeySize){
            while (sb.length()<SecretKeySize){
                sb.append(" ");
            }
        }
        try {
            byte[] data=sb.toString().getBytes(Encode);
            return new SecretKeySpec(data, EncryptAlg);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 创建16位向量: 不够则用0填充
     * @return
     */
    private static IvParameterSpec createIV() {
        StringBuffer sb = new StringBuffer(IVSize);
        sb.append(SecretKey);
        if (sb.length()>IVSize){
            sb.setLength(IVSize);
        }
        if (sb.length()<IVSize){
            while (sb.length()<IVSize){
                sb.append("0");
            }
        }
        byte[] data=null;
        try {
             data=sb.toString().getBytes(Encode);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new IvParameterSpec(data);
    }

    /**
     * 加密:有向量16位,结果转base64
     * @param context
     * @return
     */
    public static String encrypt(String context) {
        try {
            byte[] content=context.getBytes(Encode);
            SecretKeySpec key = createKey();
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.ENCRYPT_MODE, key, createIV());
            byte[] data = cipher.doFinal(content);
            String result=Base64.encodeBase64String(data);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密
     * @param context
     * @return
     */
    public static String decrypt(String context) {
        try {
            byte[] data=Base64.decodeBase64(context);
            SecretKeySpec key = createKey();
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.DECRYPT_MODE, key, createIV());
            byte[] content = cipher.doFinal(data);
            String result=new String(content,Encode);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) throws Exception{
        //密钥 加密内容(对象序列化后的内容-json格式字符串)
        String content="{\"domain\":{\"method\":\"getDetails\",\"url\":\"http://www.baidu.com\"},\"name\":\"steadyjack_age\",\"age\":\"23\",\"address\":\"Canada\",\"id\":\"12\",\"phone\":\"15627284601\"}";

        String encryptText=encrypt(content);
        String decryptText=decrypt(encryptText);
        System.out.println(String.format("明文:%s \n加密结果:%s \n解密结果:%s ",content,encryptText,decryptText));
    }
}

   测试结果如下:

明文:{"domain":{"method":"getDetails","url":"http://www.baidu.com"},"name":"steadyjack_age","age":"23","address":"Canada","id":"12","phone":"15627284601"} 
加密结果:lh/Z4sMCweHTKzpXCrIIa7UNW+t+XBwiHz9XgDiv7ETofEJAGq7FDYaWRaeAaoAbgyuaV/vcp7/W8p07z3u7uR5Q9PMPznpgQUM/pVjoE5yO51viwEb/7QYYttRsHQAqHberj8MQEs4I7qGrdkD36bvCnXYLDpPXvkLt1LwP2VD7VXhf+eQCCsxsUilxcVv+rGa+q1QuamrfGKLzlBCkiA== 
解密结果:{"domain":{"method":"getDetails","url":"http://www.baidu.com"},"name":"steadyjack_age","age":"23","address":"Canada","id":"12","phone":"15627284601"} 

    附注:CBC模式需要有密钥以及初始化向量的!

    彩蛋

    好了,关于AES的加密模式我就介绍这几种吧,对于当中代码有需要讨论或者有其他加解密算法需要实现的可以下面或者欢迎加群讨论:java开源技术交流群-583522159-我是debug

   另外,如有感兴趣的童鞋,可以关注我的微信公众号,里面会不时的分享一些干货跟福利哦:

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修罗debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值