前后端配合使用AES/CBC/PKCS7Padding 实现加解密数据(crypto-js、bouncycastle-java)

简述

  • 如题,最近项目需要选择一套对称加密算法,来满足前后端之间的加解密操作。
  • 初步打算前端使用crypto-js来实现,后端使用java本身的加密算法实现
  • 但,遇到了一个问题:java本身只支持NoPadding和PKCS5Padding,如下图:
    在这里插入图片描述
  • 而crypto-js提供的padding包括如下图,没有PKCS5Padding,所以不得以,前后端最终使用PKCS7Padding来实现功能
    在这里插入图片描述

前端

  • npm i crypto-js
    在这里插入图片描述
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script type="text/javascript" src="node_modules/crypto-js/crypto-js.js"></script>
</head>
<body>
    <script type="text/javascript">
        // 偏移量
        let iv = "0000000000000000";
        // 加密内容
        let message = "abcd中文测试加标点符号!@#¥%……&*(+——)(*&~,。,;,,/;lkk;ki;'[p]./,'\\467646789";
        // 密钥,长度必须为16
        let secret_key = "1234567890123456";

        // utf-8 转换
        message = CryptoJS.enc.Utf8.parse(message);
        secret_key = CryptoJS.enc.Utf8.parse(secret_key);
        iv = CryptoJS.enc.Utf8.parse(iv);
       
        // Encrypt
        var ciphertext = CryptoJS.AES.encrypt(message, secret_key, { 
            iv: iv,
            mode: CryptoJS.mode.CBC, 
            padding: CryptoJS.pad.Pkcs7
        });
        console.log(ciphertext.toString());

        // Decrypt
        var bytes  = CryptoJS.AES.decrypt(ciphertext.toString(), secret_key,{
            iv: iv,
            mode: CryptoJS.mode.CBC, 
            padding: CryptoJS.pad.Pkcs7 
        });
        console.log(bytes.toString(CryptoJS.enc.Utf8));
    </script> 
    
</body>
</html>
  • 运行结果:
    在这里插入图片描述

java

package com.sgcc.atc.importUser.Utils.encrypted;

import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
 
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

 
public class AESpkcs7paddingUtil {
	
	/**
	 * 密钥算法
	 */
	private static final String KEY_ALGORITHM = "AES";
	
	/**
	 * 加密/解密算法 / 工作模式 / 填充方式
	 * Java 6支持PKCS5Padding填充方式
	 * Bouncy Castle支持PKCS7Padding填充方式
	 */
	private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
	
	/**
	 * 偏移量,只有CBC模式才需要
	 */
	private final static String ivParameter = "0000000000000000";
	
	/**
	 * AES要求密钥长度为128位或192位或256位,java默认限制AES密钥长度最多128位
	 */
	public static String sKey="" ;
	
	/**
	 * 编码格式
	 */
	public static final String ENCODING = "utf-8";
	
	
	static {
		//如果是PKCS7Padding填充方式,则必须加上下面这行
		Security.addProvider(new BouncyCastleProvider());
	}
	
	/**
	 * AES加密
	 * @param source	源字符串
	 * @param key	密钥
	 * @return	加密后的密文
	 * @throws Exception
	 */
	public static String encrypt(String source, String key) throws Exception {
		byte[] sourceBytes = source.getBytes(ENCODING);
		byte[] keyBytes = key.getBytes(ENCODING);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");  
        IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes(ENCODING));
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM),iv);  
        byte[] decrypted = cipher.doFinal(sourceBytes);
        return Base64.encodeBase64String(decrypted);
	}
	
	/**
	 * AES解密
	 * @param encryptStr	加密后的密文
	 * @param key	密钥
	 * @return	源字符串
	 * @throws Exception
	 */
	public static String decrypt(String encryptStr, String key) throws Exception {
		byte[] sourceBytes = Base64.decodeBase64(encryptStr);
		byte[] keyBytes = key.getBytes(ENCODING);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");  
        IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes(ENCODING));
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM),iv);  
        byte[] decoded = cipher.doFinal(sourceBytes);  
        return new String(decoded, ENCODING);
	}
	
	
	public static void main(String[] args) throws Exception {
		String key = "1234567890123456";
		
		// 加密
		long lStart = System.currentTimeMillis();
		String enString = AESpkcs7paddingUtil.encrypt("abcd中文测试加标点符号!@#¥%……&*(+——)(*&~,。,;,,/;lkk;ki;'[p]./,'\\467646789",key);
		System.out.println("加密后的字串是:" + enString);

		long lUseTime = System.currentTimeMillis() - lStart;
		System.out.println("加密耗时:" + lUseTime + "毫秒");
		
		// 解密
		lStart = System.currentTimeMillis();
		String DeString = AESpkcs7paddingUtil.decrypt(enString,key);
		System.out.println("解密后的字串是:" + DeString);
		lUseTime = System.currentTimeMillis() - lStart;
		System.out.println("解密耗时:" + lUseTime + "毫秒");
	}
	
}
  • 运行结果
    在这里插入图片描述
    ** 注意**,因为使用了BouncyCastleProvider和base64的方法,需要引入以下两个jar包

最后

  • 上网查pkcs7 pkcs5区别的时候,发现网上说二者区别不太大,主要就是填充的块的大小不一致,所以java端尝试使用了PKCS5Padding来实现相同的功能,发现还真的可以。不过为了规避风险,最终项目还是选择了上述方案。
  • 如果你想降低加密算法难度,提高加解密速度,可以把模式修改成ECB,然后注释掉偏移量即可。

参考

https://www.cnblogs.com/chen-lhx/p/6233954.html
https://www.npmjs.com/package/crypto-js
https://blog.csdn.net/test1280/article/details/75268255
https://blog.csdn.net/sunqiujing/article/details/75065218

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值