前后端配合使用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM