全后端交互数据加密

前后端交互

  • 通信请求使用https
  • 对请求参数进行签名,防止数据篡改
  • 对请求参数以及响应数据进行加解密
  • app中使用ssl pinning防止抓包操作

https协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-78n9M2PH-1677252127361)(安全.assets/https加密流程.jpg)]

签名参数

加签和验签:发送方将请求参数通过加密算法生成一个sign值,放到请求参数里;接收方收到请求后,使用同样的方式对请求参数进行加密得到一个sign值,只要两个sign值相同,就说明参数没有被篡改。

加签操作步骤:

  1. 将所有参数(除了sign本身,以及值是空的参数)按参数键字母升序排序。
  2. 然后把排序后的参数按 参数1值1 参数2值2 … 参数n值n 的方式拼接成一个字符串。
  3. 在上一步得到的字符串前面加上密钥key,然后计算md5值,得到32位字符串,然后转成大写,得到的字符串作为sign的值放到请求参数里。

验签操作步骤:

同 加签操作步骤

现在假设需要传输的数据:/guest/rechargeNotify?p2=v2&p1=v1&method=cancel&p3=&pn=vn(实际情况最好是通过post方式发送)

  1. 拼接字符串,首先去除值是空的参数p3,剩下p2=v2&p1=v1&method=cancel&pn=vn,然后按参数名字符升序排序得到字符串:method=cancel&p1=v1&p2=v2&pn=vn。
  2. 然后做参数名和值的拼接,最后得到methodcancelp1v1p2v2pnvn。
  3. 在上面拼接得到的字符串前面加上验证密钥key,假设是abc,得到新的字符串abcmethodcancelp1v1p2v2pnvn。
  4. 将上面得到的字符串进行md5计算,假设得到的是abcdef,然后转为大写,得到ABCDEF这个值即为sign签名值。最终产生的url应该如下:/guest/rechargeNotify?p2=v2&p1=v1&method=cancel&p3=&pn=vn&sign=ABCDEF

MD5加密:

public String getSignMD5(String param, String sign) {
    StringBuffer str = new StringBuffer(sign);
    if(StringUtils.isNotEmpty(param)){
        JSONObject obj = JSONObject.parseObject(param);
        List<String> keys = new ArrayList<String>(obj.keySet());
        keys.remove(ApiSignUtil.SIGN);
        Collections.sort(keys);
        for (String key : keys) {
            Object value = obj.get(key);
            if (value == null) {
                value = "";
            }
            str.append(value);
        }
    }
    return getMd5(str.toString());
}

public String getMd5(String plainText) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(plainText.getBytes());
        byte[] b = md.digest();
        StringBuilder buf = new StringBuilder();
        byte[] var5 = b;
        int var6 = b.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            byte aB = var5[var7];
            int i = aB;
            if (aB < 0) {
                i = aB + 256;
            }

            if (i < 16) {
                buf.append("0");
            }

            buf.append(Integer.toHexString(i));
        }

        return buf.toString();
    } catch (NoSuchAlgorithmException var9) {
        var9.printStackTrace();
        return "";
    }
}

加解密请求响应数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bfRrbNux-1677252127362)(安全.assets/1648114154350.png)]

前后端交互常用的通信加密方案:

客户端:

  1. 生成AES密钥,并保存AES密钥;
  2. 用AES密钥对请求传输数据进行加密;
  3. 使用RSA公钥对AES密钥加密,然后把值放到自定义的请求头;
  4. 向服务器发起请求;

服务端:

  1. 拿到自定义的请求头,用RSA私钥解密,得到AES密钥;
  2. 使用AES密钥对请求数据进行解密;
  3. 使用AES密钥对响应数据进行加密;
  4. 向客户端发送响应;

AES对称加密

AES加解密总共有以下这些

算法/模式/填充                 字节加密后数据长度       不满16字节加密后长度  
AES/CBC/NoPadding                   16                          不支持  
AES/CBC/PKCS5Padding                32                          16  
AES/CBC/ISO10126Padding             32                          16  
AES/CFB/NoPadding                   16                          原始数据长度  
AES/CFB/PKCS5Padding                32                          16  
AES/CFB/ISO10126Padding             32                          16  
AES/ECB/NoPadding                   16                          不支持  
AES/ECB/PKCS5Padding                32                          16  
AES/ECB/ISO10126Padding             32                          16  
AES/OFB/NoPadding                   16                          原始数据长度  
AES/OFB/PKCS5Padding                32                          16  
AES/OFB/ISO10126Padding             32                          16  
AES/PCBC/NoPadding                  16                          不支持  
AES/PCBC/PKCS5Padding               32                          16  
AES/PCBC/ISO10126Padding            32                          16

AES/ECB

  • 不带模式和填充来获取AES算法的时候,其默认使用AES/ECB/PKCS5Padding(输入可以不是16字节,也不需要向量)
Cipher cipher = Cipher.getInstance("AES");

AES/ECB/PKCS5Padding

        String content = "在线助手";
        // 生成密钥需要的密码值
        String key = "www.it399.com";
        /**
         * AES加密方式一:AES不指定模式和填充,默认为 ECB/PKCS5Padding
         *
         * 不能使用填充向量
         * java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV
         */
        System.out.println("【0】AES不指定模式和填充,默认为 ECB/PKCS5Padding,输入可以不是16字节,也不需要填充向量\n");
        //128
        byte[] encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_DEFAULT);
        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_DEFAULT);
        //192
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_DEFAULT);
        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_DEFAULT);
        //256
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_DEFAULT);
        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_DEFAULT);

AES/CBC

  • 输入必须是16字节,不然报错 javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
  • CBC模式必须提供初始向量IvParameterSpec,不然报错 java.security.InvalidKeyException: Parameters missing
content: 在线助手
key: www.it399.com111
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1041)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:1009)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
    at com.csy.spring.it399.controller.encode.aes.AESUtil.encrypt(AESUtil.java:80)
    at com.csy.spring.it399.controller.encode.aes.AESUtil.main(AESUtil.java:200)
java.security.InvalidKeyException: Parameters missing
    at com.sun.crypto.provider.CipherCore.init(CipherCore.java:470)
    at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:313)
    at javax.crypto.Cipher.implInit(Cipher.java:802)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
    at javax.crypto.Cipher.init(Cipher.java:1249)
    at javax.crypto.Cipher.init(Cipher.java:1186)
    at com.csy.spring.it399.controller.encode.aes.AESUtil.decrypt(AESUtil.java:117)
    at com.csy.spring.it399.controller.encode.aes.AESUtil.main(AESUtil.java:202)
encode: null
decode: null

初始化加密模式的时改成

 Cipher cipher = Cipher.getInstance(AES/CBC/NoPadding);

java.security.InvalidKeyException: Parameters missing解决办法:

if (modeAndPadding.equals(EncodeType.AES_CBC_NoPadding)) {
    //指定一个初始化向量 (Initialization vector,IV), IV 必须是16位
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(getIV()));
} else {
    cipher.init(Cipher.ENCRYPT_MODE, keySpec);
}

AES/CBC/NoPadding, AES/CBC/PKCS5Padding, AES/CBC/ISO10126Padding

/**
         * 1.1 AES/CBC
         * AES/CBC/NoPadding
         * AES/CBC/PKCS5Padding
         * AES/CBC/ISO10126Padding
         */
System.out.println("【1.1】AES_CBC_NoPadding模式");
content = "在线助手在线助手在线助手在线助手";
key = "www.it399.com";
encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);

AES/CFB

  • 需要向量,不然报如下错误

    java.security.InvalidKeyException: Parameters missing
    
  • AES/CFB/NoPadding,AES/CFB/PKCS5Padding,AES/CFB/ISO10126Padding

    /**
           * 1.2 AES/CFB
           * AES/CBC/NoPadding
           * AES/CBC/PKCS5Padding
           * AES/CBC/ISO10126Padding
           */
    System.out.println("【1.2】AES_CFB_NoPadding模式\n");
    content = "在线助手";
    // 生成密钥需要的密码值
    key = "https://www.it399.com";
    encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
    encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
    encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
    encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
    encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
    encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
    

AES/ECB

  • AES/ECB不要填充变量,不然会报如下错误

    java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV
    

AES/ECB/NoPadding,AES/ECB/PKCS5Padding,AES/ECB/ISO10126Padding

 /**
   * 1.3 AES/ECB
   * AES/ECB/NoPadding
   * AES/ECB/PKCS5Padding
   * AES/ECB/ISO10126Padding
   */
  System.out.println("【1.3】AES_ECB模式");
  content = "在线助手";
  // 生成密钥需要的密码值
  key = "https://www.it399.com";
  encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);
  encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);
  encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);
  encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);
  encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);
  encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);

完整代码

AESUtil .java

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
 * 在线助手|在线工具|在线生成|在线制作
 * https://www.it399.com/
 * 在线助手博客
 * https://www.it399.com/blog/index
 */
public class AESUtil {
    public static final String CHARSET = "UTF-8";
    private static byte[] encryptOrDecrypt(int mode,byte[] byteContent, String key,byte[] iv, AESType type, String modeAndPadding) throws InvalidKeyException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        //此处解决mac,linux报错
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(key.getBytes());
        kgen.init(type.value, random);
        SecretKey secretKey = kgen.generateKey();
        byte[] enCodeFormat = secretKey.getEncoded();
        SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, "AES");
        Cipher cipher = Cipher.getInstance(modeAndPadding);// 创建密码器
        if ( null !=iv ) {
            //指定一个初始化向量 (Initialization vector,IV), IV 必须是16位
            cipher.init(mode, keySpec, new IvParameterSpec(iv));
        } else {
            cipher.init(mode, keySpec);
        }
        byte[] result = cipher.doFinal(byteContent);
        return result;
    }
    public static void main(String[] args) throws Exception {
//        System.out.println("【1】AES不指定模式和填充,默认为 ECB/PKCS5Padding,输入可以不是16字节,也不需要填充向量\n");
//        // 需要加密的内容
//        String content = "在线助手";
//        // 生成密钥需要的密码值
//        String key = "www.it399.com111";
//        System.out.println("content: " + content + "\nkey: " + key);
//        byte[] encodeByte;
//        byte[] decodeByte;
//        //默认方式  每次加密都不一样,但是秘钥是一样的,所以解密还是一样的
//        // 内容加密后的值
//        encodeByte = encrypt(content.getBytes(CHARSET), key, AESType.AES_128, EncodeType.AES_DEFAULT);
//        String encodeStr = TypeConvert.bytesToHexString(encodeByte);
//        // 被加密的内容解密后的值
//        decodeByte = decrypt(encodeByte, key, AESType.AES_128, EncodeType.AES_DEFAULT);
//        String decodeStr = new String(decodeByte,CHARSET);
//        System.out.println("encode: " + encodeStr + "\ndecode: " + decodeStr);
//
//
//        System.out.println("【2】AES_CBC_NoPadding模式,输入必须是16*n字节,需要填充向量\n");
//        // 内容加密后的值
//        //待加密内容不足16*n位 报错javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
//        //需要填充向量,不然报错java.security.InvalidKeyException: Parameters missing
//        //得到加密后的内容先base64编码再解码再传给解码,不然直接转回乱码
//        content = "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈";
//        encodeByte = encrypt(content.getBytes(CHARSET), key, AESType.AES_256, EncodeType.AES_CBC_NoPadding);
//        encodeStr = TypeConvert.bytesToHexString(encodeByte);
//        decodeByte = decrypt(TypeConvert.hexStringToBytes(encodeStr), key, AESType.AES_256, EncodeType.AES_CBC_NoPadding);
//        decodeStr = new String(decodeByte,CHARSET);
//        System.out.println("encode: " + encodeStr + "\ndecode: " + decodeStr);
        String content = "在线助手";
        // 生成密钥需要的密码值
        String key = "www.it399.com";
        byte[] encrypt;
        /**
         * AES加密方式一:AES不指定模式和填充,默认为 ECB/PKCS5Padding
         */
//        System.out.println("【0】AES不指定模式和填充,默认为 ECB/PKCS5Padding,输入可以不是16字节,也不需要填充向量\n");
//        //128
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_DEFAULT);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_DEFAULT);
//        //192
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_DEFAULT);
//        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_DEFAULT);
//        //256
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_DEFAULT);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_DEFAULT);
//        /**
//         * 1.1 AES/CBC (需要填充向量16*n)
//         * AES/CBC/NoPadding
//         * AES/CBC/PKCS5Padding
//         * AES/CBC/ISO10126Padding
//         */
//        System.out.println("【1.1】AES_CBC_NoPadding模式,需要填充向量,待加密必须是16*n");
//        content = "在线助手在线助手在线助手在线助手";
//        key = "www.it399.com";
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
//        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
//        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
//        /**
//         * 1.2 AES/CFB
//         * AES/CBC/NoPadding
//         * AES/CBC/PKCS5Padding
//         * AES/CBC/ISO10126Padding
//         */
//        System.out.println("【1.2】AES_CFB_NoPadding模式\n");
//        content = "在线助手";
//        // 生成密钥需要的密码值
//        key = "https://www.it399.com";
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
//        /**
//         * 1.2 AES/ECB
//         * AES/ECB/NoPadding
//         * AES/ECB/PKCS5Padding
//         * AES/ECB/ISO10126Padding
//         */
//        System.out.println("【1.3】AES_ECB模式");
//        content = "在线助手";
//        // 生成密钥需要的密码值
//        key = "https://www.it399.com";
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_ECB_PKCS5Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_ECB_PKCS5Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_ECB_PKCS5Padding);
        /**
         * 1.4 AES/OFB
         * AES/OFB/NoPadding
         * AES/OFB/PKCS5Padding
         * AES/OFB/ISO10126Padding
         */
        System.out.println("【1.4】AES_OFB模式");
        content = "在线助手";
        // 生成密钥需要的密码值
        key = "https://www.it399.com";
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_OFB_PKCS5Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_OFB_PKCS5Padding);
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_OFB_PKCS5Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_OFB_PKCS5Padding);
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_OFB_PKCS5Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_OFB_PKCS5Padding);
        /**
         * 1.5 AES/PCBC
         * AES/PCBC/NoPadding
         * AES/PCBC/PKCS5Padding
         * AES/PCBC/ISO10126Padding
         */
        System.out.println("【1.5】AES_PCBC模式");
        content = "在线助手";
        // 生成密钥需要的密码值
        key = "https://www.it399.com";
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_PCBC_PKCS5Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_PCBC_PKCS5Padding);
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_PCBC_PKCS5Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_PCBC_PKCS5Padding);
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_PCBC_PKCS5Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_PCBC_PKCS5Padding);
        //
//        /**1.3 AES/CBC
//         * AES_CBC_NoPadding模式(填充向量可选)
//         */
        System.out.println("【1.3】AES_CBC_NoPadding模式");
        content = "在线助手在线助手在线助手在线助手";
        // 生成密钥需要的密码值
        key = "www.it399.com";
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_128,EncodeType.AES_CBC_NoPadding);
        content = "在线助手";
        // 生成密钥需要的密码值
        key = "www.it399.com";
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_192,EncodeType.AES_CBC_PKCS5Padding);
        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
        encryptOrdecrypt(false,encrypt,key,getIV(),AESType.AES_256,EncodeType.AES_CBC_ISO10126Padding);
//
//
//
//        /**
//         * 2.1 AES/CFB 128/192/256位加解密
//         * AES_CFB_NoPadding模式(填充向量可选)
//         */
//        System.out.println("【2.1】AES_CFB_NoPadding模式,需要填充向量\n");
//        content = "在线助手";
//        // 生成密钥需要的密码值
//        key = "www.it399.com";
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_CFB_NoPadding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_CFB_NoPadding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_CFB_NoPadding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_CFB_NoPadding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_CFB_NoPadding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_CFB_NoPadding);
//
//        /**
//         * 2.2 AES/CFB
//         * AES_CFB_NoPadding模式(填充向量可选)
//         */
//        System.out.println("【2.2】AES_CFB_NoPadding模式\n");
//        content = "在线助手";
//        // 生成密钥需要的密码值
//        key = "www.it399.com";
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_CFB_PKCS5Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_CFB_PKCS5Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_CFB_PKCS5Padding);
//
//        /**2.3 AES/CFB
//         * AES_CFB_NoPadding模式(填充向量可选)
//         */
//        System.out.println("【2.3】AES_CFB_NoPadding模式\n");
//        content = "在线助手";
//        // 生成密钥需要的密码值
//        key = "www.it399.com";
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_128,EncodeType.AES_CFB_ISO10126Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_128,EncodeType.AES_CFB_ISO10126Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_192,EncodeType.AES_CFB_ISO10126Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_192,EncodeType.AES_CFB_ISO10126Padding);
//        encrypt = encryptOrdecrypt(true,content.getBytes(CHARSET),key,null,AESType.AES_256,EncodeType.AES_CFB_ISO10126Padding);
//        encryptOrdecrypt(false,encrypt,key,null,AESType.AES_256,EncodeType.AES_CFB_ISO10126Padding);
    }
    /**
     *
     * @param isEncrypt
     * @param source
     * @param key
     * @param type
     * @param encodeType
     */
    public static byte[] encryptOrdecrypt(boolean isEncrypt,byte[] source,String key,byte[] iv,AESType type,String encodeType) throws UnsupportedEncodingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
        if (isEncrypt){
            byte[] encodeByte = encryptOrDecrypt(Cipher.ENCRYPT_MODE,source,key,iv,type,encodeType);
            String encodeStr = TypeConvert.bytesToHexString(encodeByte);
            return encodeByte;
        }else{
            byte[] decodeByte = encryptOrDecrypt(Cipher.DECRYPT_MODE,source, key,iv,type, encodeType);
            String decodeStr = new String(decodeByte,CHARSET);
            return decodeByte;
        }
    }
    /**
     * 指定一个初始化向量 (Initialization vector,IV),IV 必须是16位
     */
    public static final byte[] getIV() throws Exception {
        return "1234567812345678".getBytes(CHARSET);
    }

EncodeType

/**
 * 在线助手|在线工具|在线生成|在线制作
 * https://www.it399.com/
 * 在线助手博客
 * https://www.it399.com/blog/index
 */
public class EncodeType {
//    算法/模式/填充                 16字节加密后数据长度       不满16字节加密后长度
//    AES/CBC/NoPadding                   16                          不支持
//    AES/CBC/PKCS5Padding                32                          16
//    AES/CBC/ISO10126Padding             32                          16
//    AES/CFB/NoPadding                   16                          原始数据长度
//    AES/CFB/PKCS5Padding                32                          16
//    AES/CFB/ISO10126Padding             32                          16
//    AES/ECB/NoPadding                   16                          不支持
//    AES/ECB/PKCS5Padding                32                          16
//    AES/ECB/ISO10126Padding             32                          16
//    AES/OFB/NoPadding                   16                          原始数据长度
//    AES/OFB/PKCS5Padding                32                          16
//    AES/OFB/ISO10126Padding             32                          16
//    AES/PCBC/NoPadding                  16                          不支持
//    AES/PCBC/PKCS5Padding               32                          16
//    AES/PCBC/ISO10126Padding            32                          16
    //默认为 ECB/PKCS5Padding
    public final static String AES_DEFAULT = "AES";
    public final static String AES_CBC_NoPadding = "AES/CBC/NoPadding";
    public final static String AES_CBC_PKCS5Padding = "AES/CBC/PKCS5Padding";
    public final static String AES_CBC_ISO10126Padding = "AES/CBC/ISO10126Padding";
    public final static String AES_CFB_NoPadding = "AES/CFB/NoPadding";
    public final static String AES_CFB_PKCS5Padding = "AES/CFB/PKCS5Padding";
    public final static String AES_CFB_ISO10126Padding = "AES/CFB/ISO10126Padding";
    public final static String AES_ECB_NoPadding = "AES/ECB/NoPadding";
    public final static String AES_ECB_PKCS5Padding = "AES/ECB/PKCS5Padding";
    public final static String AES_ECB_ISO10126Padding = "AES/ECB/ISO10126Padding";
    public final static String AES_OFB_NoPadding = "AES/OFB/NoPadding";
    public final static String AES_OFB_PKCS5Padding = "AES/OFB/PKCS5Padding";
    public final static String AES_OFB_ISO10126Padding = "AES/OFB/ISO10126Padding";
    public final static String AES_PCBC_NoPadding = "AES/PCBC/NoPadding";
    public final static String AES_PCBC_PKCS5Padding = "AES/PCBC/PKCS5Padding";
    public final static String AES_PCBC_ISO10126Padding = "AES/PCBC/ISO10126Padding";
}

另附代码

package net.trueland.employee.common;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

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

import com.alibaba.fastjson.JSONObject;

public class SecurityUtils {

    public static final String ALGORITHM_AES = "AES";
    public static final String CHARSET_UTF8 = "UTF-8";

    /**
     * 参数加签
     * @param json 参数,仅支持json对象
     * @param key 密钥
     * @return 密文
     */
    public static String signParam(String json, String key) throws Exception {
        if (StringUtils.isBlank(json)) {
            return StringUtils.EMPTY;
        }
        if (StringUtils.isBlank(key)) {
            throw new Exception("param add sign error: no key");
        }
        StringBuffer stringBuffer = new StringBuffer(key);
        JSONObject jsonObject = JSONObject.parseObject(json);
        List<String> keyList = new ArrayList<String>(jsonObject.keySet());
        keyList.remove("sign");
        Collections.sort(keyList);
        for (String s : keyList) {
            Object o = jsonObject.get(s);
            if (o == null) {
                o = "";
            }
            stringBuffer.append(s);
            stringBuffer.append(o);
        }
        return stringBuffer.toString();
    }

    /**
     * md5 加密
     * @param str 明文
     * @return
     */
    public static String encryptMD5(String str) {
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            // TODO 异常处理
        }

        md.update(str.getBytes());
        byte[] b = md.digest();
        StringBuilder buf = new StringBuilder();
        byte[] var5 = b;
        int var6 = b.length;

        for (int var7 = 0; var7 < var6; ++var7) {
            byte aB = var5[var7];
            int i = aB;
            if (aB < 0) {
                i = aB + 256;
            }
            if (i < 16) {
                buf.append("0");
            }
            buf.append(Integer.toHexString(i));
        }
        return buf.toString();
    }

    // ------------------------------------------AES/ECB/PKCS5Padding加解密, AES默认的加密方式--------------------------------------------------
    /**
     * 加密速度快
     * 该模式不可以使用向量,否则报错:java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV
     * @param plaintext 明文
     * @param key 密钥 不强制位数
     * @return 密文
     */
    public static String encrypt(String plaintext, String key) throws Exception {
        return encryptOrDecrypt(Cipher.ENCRYPT_MODE, plaintext, key, null, "AES/ECB/PKCS5Padding");
    }

    /**
     * 加密速度快
     * 该模式不可以使用向量,否则报错:java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV
     * @param ciphertext 密文
     * @param key 密钥 不强制位数
     * @return 明文
     */
    public static String decrypt(String ciphertext, String key) throws Exception {
        return encryptOrDecrypt(Cipher.DECRYPT_MODE, ciphertext, key, null, "AES/ECB/PKCS5Padding");
    }

    // ------------------------------------------AES/CBC/PKCS5Padding 加解密--------------------------------------------------
    /**
     * 安全性高,加密速度慢
     * CBC模式必须提供向量,否则报错:java.security.InvalidKeyException: Parameters missing
     * @param plaintext 明文
     * @param key 密钥,不强制位数
     * @param offset 偏移量,提高安全性,强制16位
     * @return 密文
     */
    public static String encrypt(String plaintext, String key, String offset) throws Exception {
        return encryptOrDecrypt(Cipher.ENCRYPT_MODE, plaintext, key, offset.getBytes(CHARSET_UTF8), "AES/CBC/PKCS5Padding");
    }

    /**
     * 安全性高,加密速度慢
     * CBC模式必须提供向量,否则报错:java.security.InvalidKeyException: Parameters missing
     * @param ciphertext 密文
     * @param key 密钥,不强制位数
     * @param offset 偏移量,提高安全性,强制16位
     * @return 明文
     */
    public static String decrypt(String ciphertext, String key, String offset) throws Exception {
        return encryptOrDecrypt(Cipher.DECRYPT_MODE, ciphertext, key, offset.getBytes(CHARSET_UTF8), "AES/CBC/PKCS5Padding");
    }

    /**
     * 加密或解密
     * @param mode 加密1 解密2
     * @param content 明文或密文
     * @param key 密钥
     * @param iv 向量
     * @param modeAndPadding 加密类型和填充类型
     * @return
     * @throws Exception
     */
    private static String encryptOrDecrypt(int mode, String content, String key, byte[] iv, String modeAndPadding) throws Exception {
        byte[] byteContent = Cipher.ENCRYPT_MODE == mode ? content.getBytes(CHARSET_UTF8) : Base64.decodeBase64(content);
        // 密钥生成器
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        // 随机数,不可以随时间随机,否则mac,linux报错
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        // 设置随机种子
        random.setSeed(key.getBytes(CHARSET_UTF8));
        // 初始化密钥生成器
        keyGenerator.init(128, random);
        // 生成密钥
        SecretKey secretKey = keyGenerator.generateKey();
        // 生成AES专用密钥
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), ALGORITHM_AES);

        // 创建加/解密器
        Cipher cipher = Cipher.getInstance(modeAndPadding);
        // 初始化加/解密器
        if (null != iv){
            // 指定向量,提高安全性
            cipher.init(mode,secretKeySpec,new IvParameterSpec(iv));
        }else {
            cipher.init(mode, secretKeySpec);
        }
        // 加密/解密
        byte[] result = cipher.doFinal(byteContent);
        return Cipher.ENCRYPT_MODE == mode ? Base64.encodeBase64String(result) : new String(result);
    }
    
     /**
     * 随机生成指定长度的密钥
     * @param length
     */
    public static String randomKey(int length){
        return RandomStringUtils.randomAlphanumeric(length);
    }
}

RSA非对称加密

public class RSAEncrypt {
	private static Map<Integer, String> keyMap = new HashMap<Integer, String>();  //用于封装随机产生的公钥与私钥
	public static void main(String[] args) throws Exception {
		//生成公钥和私钥
		genKeyPair();
		//加密字符串
		String message = "df723820";
		System.out.println("随机生成的公钥为:" + keyMap.get(0));
		System.out.println("随机生成的私钥为:" + keyMap.get(1));
		String messageEn = encrypt(message,keyMap.get(0));
		System.out.println(message + "\t加密后的字符串为:" + messageEn);
		String messageDe = decrypt(messageEn,keyMap.get(1));
		System.out.println("还原后的字符串为:" + messageDe);
	}

	/** 
	 * 随机生成密钥对 
	 * @throws NoSuchAlgorithmException 
	 */  
	public static void genKeyPair() throws NoSuchAlgorithmException {  
		// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象  
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");  
		// 初始化密钥对生成器,密钥大小为96-1024位  
		keyPairGen.initialize(1024,new SecureRandom());  
		// 生成一个密钥对,保存在keyPair中  
		KeyPair keyPair = keyPairGen.generateKeyPair();  
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥  
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥  
		String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));  
		// 得到私钥字符串  
		String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));  
		// 将公钥和私钥保存到Map
		keyMap.put(0,publicKeyString);  //0表示公钥
		keyMap.put(1,privateKeyString);  //1表示私钥
	}  
	/** 
	 * RSA公钥加密 
	 *  
	 * @param str 
	 *            加密字符串
	 * @param publicKey 
	 *            公钥 
	 * @return 密文 
	 * @throws Exception 
	 *             加密过程中的异常信息 
	 */  
	public static String encrypt( String str, String publicKey ) throws Exception{
		//base64编码的公钥
		byte[] decoded = Base64.decodeBase64(publicKey);
		RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
		//RSA加密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, pubKey);
		String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
		return outStr;
	}

	/** 
	 * RSA私钥解密
	 *  
	 * @param str 
	 *            加密字符串
	 * @param privateKey 
	 *            私钥 
	 * @return 铭文
	 * @throws Exception 
	 *             解密过程中的异常信息 
	 */  
	public static String decrypt(String str, String privateKey) throws Exception{
		//64位解码加密后的字符串
		byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
		//base64编码的私钥
		byte[] decoded = Base64.decodeBase64(privateKey);  
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));  
		//RSA解密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, priKey);
		String outStr = new String(cipher.doFinal(inputByte));
		return outStr;
	}

}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值