AES的加密和解密
AES的基础知识
AES的加密模式(Mode)
AES的加密模式有:CBC、ECB、CTR、OCF、CFB。
其中 ECB 有安全问题,所以一定不选择。
而常用的是 CBC,并且 crypto-js 默认也用了 CBC 所以就无脑选择了 CBC
密钥的长度(Key)
AES需要指定密钥长度必须为 128 位、192 位或256 位,即字符串长度为:16、24 或 32。
初始化向量(IV)
一段固定长度的随机数,用于增强AES加密的强度。
IV的长度通常为16字节(即128位),它必须与密钥一起使用。
填充方式(Padding)
由于AES加密的块大小通常为128位,而明文的长度可能不是块大小的整数倍,因此需要进行填充。
常见的填充方式有PKCS#5和PKCS#7,它们可以保证明文长度为块大小的整数倍。
AES加密解密示例
php 示例
1$key = 'a7gE3fH9jKmN1pQ2rS4tU6vY8zW9xL01';
2$iv = '7hJ3kQxZW45mNpR2';
3$data = '123456';
4$encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
5echo base64_encode($encrypted), PHP_EOL;
6$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
7echo $decrypted;
javascript 示例
请先引入CryptoJS:<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
1 const key = CryptoJS.enc.Utf8.parse('a7gE3fH9jKmN1pQ2rS4tU6vY8zW9xL01'); // 秘钥
2 const iv = CryptoJS.enc.Utf8.parse('7hJ3kQxZW45mNpR2'); // 初始化向量
3 const plaintext = "Hello, AES encryption!"; // 原文
4
5 const encryptData = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plaintext), key, {
6 iv: iv, // 初始化向量
7 mode: CryptoJS.mode.CBC, // 加密模式
8 padding: CryptoJS.pad.Pkcs7 // 填充方式
9 });
10 var ciphertext = encryptData.toString(); // 加密后的字符串
11 console.log(ciphertext);
12
13 var decryptData = CryptoJS.AES.decrypt(ciphertext, key, {
14 iv: iv, // 初始化向量
15 mode: CryptoJS.mode.CBC, // 加密模式
16 padding: CryptoJS.pad.Pkcs7 // 填充方式
17 });
18 var originalText = decryptData.toString(CryptoJS.enc.Utf8); // 解密后的字符串
19 console.log(originalText);
golang 示例
1package main
2
3import (
4 "bytes"
5 "crypto/aes"
6 "crypto/cipher"
7 "encoding/base64"
8 "fmt"
9)
10
11func PKCS7Padding(paddingBytes []byte, blockSize int) []byte {
12 padding := blockSize - len(paddingBytes)%blockSize
13 padText := bytes.Repeat([]byte{byte(padding)}, padding)
14 return append(paddingBytes, padText...)
15}
16
17func PKCS7UnPadding(unPaddingBytes []byte) []byte {
18 length := len(unPaddingBytes)
19 unPadding := int(unPaddingBytes[length-1])
20 return unPaddingBytes[:(length - unPadding)]
21}
22
23func AesEncryptCBC(originalText string, key string, iv string) string {
24 originalBytes := []byte(originalText)
25 keyBytes := []byte(key)
26 ivBytes := []byte(iv)
27
28 block, _ := aes.NewCipher(keyBytes)
29 blockSize := block.BlockSize()
30 originalBytes = PKCS7Padding(originalBytes, blockSize)
31 cipherBytes := make([]byte, len(originalBytes))
32
33 blockMode := cipher.NewCBCEncrypter(block, ivBytes[:blockSize])
34 blockMode.CryptBlocks(cipherBytes, originalBytes)
35 return base64.StdEncoding.EncodeToString(cipherBytes)
36}
37
38func AesDecryptCBC(ciphertext string, key string, iv string) string {
39 cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
40 keyBytes := []byte(key)
41 ivBytes := []byte(iv)
42
43 block, _ := aes.NewCipher(keyBytes)
44 blockSize := block.BlockSize()
45 originalBytes := make([]byte, len(cipherBytes))
46
47 blockMode := cipher.NewCBCDecrypter(block, ivBytes[:blockSize])
48 blockMode.CryptBlocks(originalBytes, cipherBytes)
49 originalBytes = PKCS7UnPadding(originalBytes)
50 return string(originalBytes)
51}
52
53func main() {
54 key := "a7gE3fH9jKmN1pQ2rS4tU6vY8zW9xL01"
55 iv := "7hJ3kQxZW45mNpR2"
56 plaintext := "Hello, AES encryption!"
57 ciphertext := AesEncryptCBC(plaintext, key, iv)
58 fmt.Println(ciphertext)
59 originalText := AesDecryptCBC(ciphertext, key, iv)
60 fmt.Println(originalText)
61}
java 示例
使用 Hutool 5.8.16
该示例采用Hutool的Crypto模块,请先在pom.xml中引入Hutool库:
1<dependency>
2 <groupId>cn.hutool</groupId>
3 <artifactId>hutool-all</artifactId>
4 <version>5.8.16</version> <!-- 版本可调整 -->
5</dependency>
示例代码:
1package com.tob.biz.qt.validator;
2
3import cn.hutool.crypto.symmetric.AES;
4
5import java.nio.charset.StandardCharsets;
6
7public class AESTest {
8 public static void main(String[] args) {
9 String key = "a7gE3fH9jKmN1pQ2rS4tU6vY8zW9xL01"; // 秘钥
10 String iv = "7hJ3kQxZW45mNpR2"; // 初始化向量
11 String plaintext = "Hello, AES encryption!"; // 原文
12
13 AES aes = new AES("CBC", "PKCS7Padding",
14 key.getBytes(StandardCharsets.UTF_8), // 密钥,可以自定义
15 iv.getBytes(StandardCharsets.UTF_8) // 初始化向量
16 );
17
18 String ciphertext = aes.encryptBase64(plaintext); // 加密后的字符串
19 System.out.println(ciphertext);
20
21 String originalText = aes.decryptStr(ciphertext); // 解密后的字符串
22 System.out.println(originalText);
23 }
24}
使用 Hutool 5.8.26
该示例采用Hutool的Crypto模块,请先在pom.xml中引入Hutool库:
1<dependency>
2 <groupId>cn.hutool</groupId>
3 <artifactId>hutool-all</artifactId>
4 <version>5.8.26</version> <!-- 版本可调整 -->
5</dependency>
示例代码:
1package com.tob.biz.qt.validator;
2
3import cn.hutool.crypto.Mode;
4import cn.hutool.crypto.Padding;
5import cn.hutool.crypto.symmetric.AES;
6
7public class AESTest {
8 public static void main(String[] args) {
9 AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, "a7gE3fH9jKmN1pQ2".getBytes()); // 秘钥
10 String originalText = "18007486225"; // 原文
11
12 System.out.println("\n********************* AES Base64 *********************");
13 String str1 = aes.encryptBase64(originalText); // 加密后的Base64密文
14 System.out.println("encrypt ======>> " + str1);
15 System.out.println("decrypt ======>> " + aes.decryptStr(str1)); // Base64密文解密后的字符串
16 // 输出结果:
17 // ********************* AES Base64 *********************
18 // encrypt ======>> 4BZ9qepXmU0Gvr33wL8dgw==
19 // decrypt ======>> 18007486225
20
21
22 System.out.println("\n********************* AES Hex *********************");
23 String str2 = aes.encryptHex(originalText); // 加密后的Hex密文
24 System.out.println("encrypt ======>> " + str2);
25 System.out.println("decrypt ======>> " + aes.decryptStr(str2)); // Hex密文解密后的字符串
26 // 输出结果:
27 // ********************* AES Hex *********************
28 // encrypt ======>> e0167da9ea57994d06bebdf7c0bf1d83
29 // decrypt ======>> 18007486225
30 }
31}
说明:相同的原文,Base64 密文和 Hex 密文是不一样的,但是解密后的内容完全一致。
MySQL 示例
注意,MySQL的AES加密、解密函数默认使用AES-128-ECB模式。
控制台查看/设置 AES 加解密模式
1mysql> show variables like '%block_encryption_mode%';
2+-----------------------+-------------+
3| Variable_name | Value |
4+-----------------------+-------------+
5| block_encryption_mode | aes-128-ecb |
6+-----------------------+-------------+
71 row in set (0.00 sec)
8
9# 设置AES加解密模式(没有必要的话,请不要手贱去改动!!!)
10# 例如:设置为使用256位的密钥长度和CBC模式
11mysql> SET GLOBAL block_encryption_mode='aes-256-cbc';
AES 加解密
1-- HEX 加密
2select HEX(AES_ENCRYPT('18007486225', 'a7gE3fH9jKmN1pQ2'))
3-- 输出结果: E0167DA9EA57994D06BEBDF7C0BF1D83
4-- 说明:
5-- AES_ENCRYPT 是加密函数,它的第一个参数是原文,第二个参数是密钥
6-- HEX 函数是把加密的结果转换为十六进制值的字符串,因为直接展示加密的结果是乱码
7-- 输出的结果和上面 java 代码中的 AES Hex 结果一致
8
9-- HEX 解密
10select aes_decrypt(unhex('E0167DA9EA57994D06BEBDF7C0BF1D83'), 'a7gE3fH9jKmN1pQ2');
11-- 输出结果: 18007486225
12-- 说明:
13-- aes_decrypt 是解密函数,它的第一个参数是密文,第二个参数是密钥
14-- unhex 函数是要把十六进制值的字符串转换为真正的密文后,才能解密
其实加解密的结果也可以使用 Base64 进行编码和解码,例如:
1select to_base64(AES_ENCRYPT('18007486225', 'a7gE3fH9jKmN1pQ2'));
2-- 输出结果: 4BZ9qepXmU0Gvr33wL8dgw==
3
4select aes_decrypt(from_base64('4BZ9qepXmU0Gvr33wL8dgw=='), 'a7gE3fH9jKmN1pQ2');
5-- 输出结果: 18007486225
评论