前后端AES加解密


前言

前端在调用后端登录接口时,传给后端的密码是明文可以直接在消息体中直接查看,领导说需要前端使用AES加密后,后端使用AES解密。


提示:以下是本篇文章正文内容,下面案例可供参考

一、AES是什么?

AES(高级加密标准,Advanced Encryption Standard)是一种对称密钥加密算法,用于保护电子数据。它是美国国家标准技术研究所(NIST)在2001年选定的加密标准,并在2002年正式成为联邦信息处理标准(FIPS)。AES是一种区块加密算法,意味着它会将数据分成固定大小的块进行加密;对于AES来说,这个块的大小是128位。AES可以使用多种长度的密钥:128位、192位或256位。

AES加解密有以下几种模式:

  1. 电子密码本模式(ECB,Electronic Codebook)

    • 每个块独立加密,适用于小量数据。
  2. 密码块链接模式(CBC,Cipher Block Chaining)

    • 前一个块的加密结果会与当前块的明文进行异或后再加密,需要一个初始化向量(IV)。
  3. 计数器模式(CTR,Counter)

    • 将一个递增的计数器与密钥一起加密,然后与明文块进行异或操作。
  4. 密码反馈模式(CFB,Cipher Feedback)

    • 将前一个块的加密结果部分用于当前块的加密,可以将区块加密算法转变为流加密算法。
  5. 输出反馈模式(OFB,Output Feedback)

    • 类似于CFB,但是使用加密算法的输出作为下一次的反馈,而不是使用加密后的密文。
  6. 密文窃取模式(CTS,CipherText Stealing)

    • 是CBC模式的变体,可以用来加密不是块大小整数倍的消息。

当涉及到块加密时,如果最后一个数据块不足一个完整的块的长度,就需要进行填充(Padding)。常见的补码格式有:

  1. PKCS#7/PKCS#5

    • 这是最常用的填充方式,适用于任何块大小。填充的每个字节的值是缺少的字节数。
  2. ANSI X.923

    • 填充的字节中,最后一个字节是缺少的字节数,其余字节为零。
  3. ISO 10126

    • 填充的字节包括随机数据,最后一个字节是缺少的字节数。
  4. ISO/IEC 7816-4

    • 填充的第一个字节是0x80(十六进制),后续字节全部为零。
  5. Zero Padding

    • 简单地用零填充剩余的空间,可能会在解密时遇到问题,因为原始数据可能以零结束。
  6. Bit Padding

    • 在需要填充的第一个位上填充1,然后填充0,直到达到块的大小。

每种模式和填充方式有其特定的用途和安全性考虑。在选择使用时,需要根据实际的安全需求和环境来做出合适的选择。

二、前端使用步骤

1.安装依赖

代码如下(示例):

npm i crypto-js

在这里插入图片描述

2.加密解密方法

代码如下(示例):

import CryptoJS from 'crypto-js'

function encode(str = ''){
  // utf-8 转换
  let message = CryptoJS.enc.Utf8.parse(str);
  let secret_key = CryptoJS.enc.Utf8.parse("1234567890123456");
  let iv = CryptoJS.enc.Utf8.parse("0000000000000000");
  // Encrypt
  var ciphertext = CryptoJS.AES.encrypt(message, secret_key, { 
      iv: iv,
      mode: CryptoJS.mode.CBC, 
      padding: CryptoJS.pad.Pkcs7
  });
  return ciphertext.toString();
}

function decode(str = ''){
  // utf-8 转换
  let message = str;
  let secret_key = CryptoJS.enc.Utf8.parse("1234567890123456");
  let iv = CryptoJS.enc.Utf8.parse("0000000000000000");
  
  // Decrypt
  var ciphertext  = CryptoJS.AES.decrypt(message.toString(), secret_key,{
      iv: iv,
      mode: CryptoJS.mode.CBC, 
      padding: CryptoJS.pad.Pkcs7 
  });
  return ciphertext.toString(CryptoJS.enc.Utf8);
}

三、后端使用步骤

1.安装依赖

代码如下(示例):

	<dependency>
	    <groupId>commons-codec</groupId>
	    <artifactId>commons-codec</artifactId>
	    <version>1.16.1</version>
	</dependency>
	<dependency>
	    <groupId>org.bouncycastle</groupId>
	    <artifactId>bcprov-jdk15to18</artifactId>
	    <version>1.68</version>
	</dependency>

2.工具类


import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

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

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("123Q1!!!!!!456.",key);
		System.out.println("加密后的字串是:" + enString);

		long lUseTime = System.currentTimeMillis() - lStart;
		System.out.println("加密耗时:" + lUseTime + "毫秒");

		// 解密
		lStart = System.currentTimeMillis();
		String DeString = AESpkcs7paddingUtil.decrypt("X9dcDawAB/Oke2cvgji3Eg==",key);
		System.out.println("解密后的字串是:" + DeString);
		lUseTime = System.currentTimeMillis() - lStart;
		System.out.println("解密耗时:" + lUseTime + "毫秒");
	}


}

这里提供一个简单的后端加密前端解密的示例(使用AES加密算法): 后端代码(Node.js): ```javascript const crypto = require('crypto'); // 加密函数 function encrypt(text, secretKey) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv('aes-256-cbc', secretKey, iv); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return iv.toString('hex') + ':' + encrypted.toString('hex'); } // 解密函数 function decrypt(text, secretKey) { const parts = text.split(':'); const iv = Buffer.from(parts.shift(), 'hex'); const encryptedText = Buffer.from(parts.join(':'), 'hex'); const decipher = crypto.createDecipheriv('aes-256-cbc', secretKey, iv); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString(); } // 测试 const secretKey = 'my-secret-key'; const originalText = 'Hello, World!'; console.log('Original text:', originalText); const encryptedText = encrypt(originalText, secretKey); console.log('Encrypted text:', encryptedText); const decryptedText = decrypt(encryptedText, secretKey); console.log('Decrypted text:', decryptedText); ``` 前端代码(JavaScript): ```javascript // 加密函数(使用 CryptoJS 库) function encrypt(text, secretKey) { const iv = CryptoJS.lib.WordArray.random(16); const encrypted = CryptoJS.AES.encrypt(text, secretKey, { iv: iv }); return iv.toString() + encrypted.toString(); } // 解密函数(使用 CryptoJS 库) function decrypt(text, secretKey) { const parts = text.split(':'); const iv = CryptoJS.enc.Hex.parse(parts.shift()); const encryptedText = parts.join(':'); const decrypted = CryptoJS.AES.decrypt(encryptedText, secretKey, { iv: iv }); return decrypted.toString(CryptoJS.enc.Utf8); } // 测试 const secretKey = 'my-secret-key'; const originalText = 'Hello, World!'; console.log('Original text:', originalText); const encryptedText = encrypt(originalText, secretKey); console.log('Encrypted text:', encryptedText); const decryptedText = decrypt(encryptedText, secretKey); console.log('Decrypted text:', decryptedText); ``` 注意:这只是一个简单的示例,实际应用中需要注意安全问题,如密钥的保存和传输等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值