mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03: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:
|
||||
# 配置文件目录
|
||||
- ./uploadfile:/uploadfile
|
||||
|
||||
# 数据库模块
|
||||
xiaozhi-esp32-server-db:
|
||||
image: mysql:latest
|
||||
container_name: xiaozhi-esp32-server-db
|
||||
@@ -73,6 +73,7 @@ services:
|
||||
- MYSQL_ROOT_PASSWORD=123456
|
||||
- MYSQL_DATABASE=xiaozhi_esp32_server
|
||||
- MYSQL_INITDB_ARGS="--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci"
|
||||
# redis模块
|
||||
xiaozhi-esp32-server-redis:
|
||||
image: redis
|
||||
expose:
|
||||
|
||||
Reference in New Issue
Block a user