前言
前端在调用后端登录接口时,传给后端的密码是明文可以直接在消息体中直接查看,领导说需要前端使用AES加密后,后端使用AES解密。
提示:以下是本篇文章正文内容,下面案例可供参考
一、AES是什么?
AES(高级加密标准,Advanced Encryption Standard)是一种对称密钥加密算法,用于保护电子数据。它是美国国家标准技术研究所(NIST)在2001年选定的加密标准,并在2002年正式成为联邦信息处理标准(FIPS)。AES是一种区块加密算法,意味着它会将数据分成固定大小的块进行加密;对于AES来说,这个块的大小是128位。AES可以使用多种长度的密钥:128位、192位或256位。
AES加解密有以下几种模式:
-
电子密码本模式(ECB,Electronic Codebook)
- 每个块独立加密,适用于小量数据。
-
密码块链接模式(CBC,Cipher Block Chaining)
- 前一个块的加密结果会与当前块的明文进行异或后再加密,需要一个初始化向量(IV)。
-
计数器模式(CTR,Counter)
- 将一个递增的计数器与密钥一起加密,然后与明文块进行异或操作。
-
密码反馈模式(CFB,Cipher Feedback)
- 将前一个块的加密结果部分用于当前块的加密,可以将区块加密算法转变为流加密算法。
-
输出反馈模式(OFB,Output Feedback)
- 类似于CFB,但是使用加密算法的输出作为下一次的反馈,而不是使用加密后的密文。
-
密文窃取模式(CTS,CipherText Stealing)
- 是CBC模式的变体,可以用来加密不是块大小整数倍的消息。
当涉及到块加密时,如果最后一个数据块不足一个完整的块的长度,就需要进行填充(Padding)。常见的补码格式有:
-
PKCS#7/PKCS#5
- 这是最常用的填充方式,适用于任何块大小。填充的每个字节的值是缺少的字节数。
-
ANSI X.923
- 填充的字节中,最后一个字节是缺少的字节数,其余字节为零。
-
ISO 10126
- 填充的字节包括随机数据,最后一个字节是缺少的字节数。
-
ISO/IEC 7816-4
- 填充的第一个字节是
0x80
(十六进制),后续字节全部为零。
- 填充的第一个字节是
-
Zero Padding
- 简单地用零填充剩余的空间,可能会在解密时遇到问题,因为原始数据可能以零结束。
-
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 + "毫秒");
}
}