mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 01:53:53 +08:00
Merge pull request #1666 from xinnan-tech/aes_utils
update:新增AES加密方法,和python端AES一致
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
package xiaozhi.common.utils;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
public class AESUtils {
|
||||||
|
|
||||||
|
private static final String ALGORITHM = "AES";
|
||||||
|
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密
|
||||||
|
*
|
||||||
|
* @param key 密钥(16位、24位或32位)
|
||||||
|
* @param plainText 待加密字符串
|
||||||
|
* @return 加密后的Base64字符串
|
||||||
|
*/
|
||||||
|
public static String encrypt(String key, String plainText) {
|
||||||
|
try {
|
||||||
|
// 确保密钥长度为16、24或32位
|
||||||
|
byte[] keyBytes = padKey(key.getBytes(StandardCharsets.UTF_8));
|
||||||
|
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||||
|
|
||||||
|
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64.getEncoder().encodeToString(encryptedBytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("AES加密失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
*
|
||||||
|
* @param key 密钥(16位、24位或32位)
|
||||||
|
* @param encryptedText 待解密的Base64字符串
|
||||||
|
* @return 解密后的字符串
|
||||||
|
*/
|
||||||
|
public static String decrypt(String key, String encryptedText) {
|
||||||
|
try {
|
||||||
|
// 确保密钥长度为16、24或32位
|
||||||
|
byte[] keyBytes = padKey(key.getBytes(StandardCharsets.UTF_8));
|
||||||
|
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||||
|
|
||||||
|
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
|
||||||
|
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
|
||||||
|
return new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("AES解密失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充密钥到指定长度(16、24或32位)
|
||||||
|
*
|
||||||
|
* @param keyBytes 原始密钥字节数组
|
||||||
|
* @return 填充后的密钥字节数组
|
||||||
|
*/
|
||||||
|
private static byte[] padKey(byte[] keyBytes) {
|
||||||
|
int keyLength = keyBytes.length;
|
||||||
|
if (keyLength == 16 || keyLength == 24 || keyLength == 32) {
|
||||||
|
return keyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果密钥长度不足,用0填充;如果超过,截取前32位
|
||||||
|
byte[] paddedKey = new byte[32];
|
||||||
|
System.arraycopy(keyBytes, 0, paddedKey, 0, Math.min(keyLength, 32));
|
||||||
|
return paddedKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package xiaozhi.common.utils;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class AESUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptAndDecrypt() {
|
||||||
|
String key = "xiaozhi1234567890";
|
||||||
|
String plainText = "Hello, 小智!";
|
||||||
|
|
||||||
|
System.out.println("原始文本: " + plainText);
|
||||||
|
System.out.println("密钥: " + key);
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
String encrypted = AESUtils.encrypt(key, plainText);
|
||||||
|
System.out.println("加密结果: " + encrypted);
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
String decrypted = AESUtils.decrypt(key, encrypted);
|
||||||
|
System.out.println("解密结果: " + decrypted);
|
||||||
|
|
||||||
|
// 验证
|
||||||
|
assertEquals(plainText, decrypted, "加解密结果应该一致");
|
||||||
|
System.out.println("加解密一致性: " + plainText.equals(decrypted));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDifferentKeyLengths() {
|
||||||
|
String[] keys = {
|
||||||
|
"1234567890123456", // 16位
|
||||||
|
"123456789012345678901234", // 24位
|
||||||
|
"12345678901234567890123456789012", // 32位
|
||||||
|
"short", // 短密钥
|
||||||
|
"verylongkeythatwillbetruncatedto32bytes" // 长密钥
|
||||||
|
};
|
||||||
|
|
||||||
|
String plainText = "测试文本";
|
||||||
|
|
||||||
|
for (String key : keys) {
|
||||||
|
String encrypted = AESUtils.encrypt(key, plainText);
|
||||||
|
String decrypted = AESUtils.decrypt(key, encrypted);
|
||||||
|
assertEquals(plainText, decrypted, "密钥长度: " + key.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSpecialCharacters() {
|
||||||
|
String key = "xiaozhi1234567890";
|
||||||
|
String[] testTexts = {
|
||||||
|
"Hello World",
|
||||||
|
"你好世界",
|
||||||
|
"Hello, 小智!",
|
||||||
|
"特殊字符: !@#$%^&*()",
|
||||||
|
"数字123和中文混合",
|
||||||
|
"Emoji: 😀🎉🚀",
|
||||||
|
"空字符串测试",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String text : testTexts) {
|
||||||
|
String encrypted = AESUtils.encrypt(key, text);
|
||||||
|
String decrypted = AESUtils.decrypt(key, encrypted);
|
||||||
|
assertEquals(text, decrypted, "测试文本: " + text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCrossLanguageCompatibility() {
|
||||||
|
// 这些是Python版本生成的加密结果,用于测试跨语言兼容性
|
||||||
|
String key = "xiaozhi1234567890";
|
||||||
|
String plainText = "Hello, 小智!";
|
||||||
|
|
||||||
|
// Python版本生成的加密结果(需要运行Python测试后获取)
|
||||||
|
// String pythonEncrypted = "从Python测试中获取的加密结果";
|
||||||
|
// String decrypted = AESUtils.decrypt(key, pythonEncrypted);
|
||||||
|
// assertEquals(plainText, decrypted, "Java应该能解密Python加密的结果");
|
||||||
|
|
||||||
|
// 生成Java加密结果供Python测试
|
||||||
|
String javaEncrypted = AESUtils.encrypt(key, plainText);
|
||||||
|
System.out.println("Java加密结果供Python测试: " + javaEncrypted);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
# 配置文件目录
|
# 配置文件目录
|
||||||
- ./uploadfile:/uploadfile
|
- ./uploadfile:/uploadfile
|
||||||
|
# 数据库模块
|
||||||
xiaozhi-esp32-server-db:
|
xiaozhi-esp32-server-db:
|
||||||
image: mysql:latest
|
image: mysql:latest
|
||||||
container_name: xiaozhi-esp32-server-db
|
container_name: xiaozhi-esp32-server-db
|
||||||
@@ -73,6 +73,7 @@ services:
|
|||||||
- MYSQL_ROOT_PASSWORD=123456
|
- MYSQL_ROOT_PASSWORD=123456
|
||||||
- MYSQL_DATABASE=xiaozhi_esp32_server
|
- MYSQL_DATABASE=xiaozhi_esp32_server
|
||||||
- MYSQL_INITDB_ARGS="--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci"
|
- MYSQL_INITDB_ARGS="--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci"
|
||||||
|
# redis模块
|
||||||
xiaozhi-esp32-server-redis:
|
xiaozhi-esp32-server-redis:
|
||||||
image: redis
|
image: redis
|
||||||
expose:
|
expose:
|
||||||
|
|||||||
Reference in New Issue
Block a user