在使用 DES 對稱加密處理中文數據時,亂碼是最常見的棘手問題。根本原因在於 字符編碼不一致:加密端、傳輸端、解密端使用了不同的字符集。本文提供標準化解決方案。
在 Servlet/Controller 返回加密數據時,必須在響應頭中明確聲明字符集:
// Spring MVC / Servlet 通用寫法
response.setContentType("application/json; charset=UTF-8");
// 或
response.setCharacterEncoding("UTF-8");
接收加密數據 (通常為 Hex 字符串) 時,需按以下順序處理:
/**
* 完整解密流程:Hex字符串 → 字節數組 → DES解密 → UTF-8字符串
* @param hexData 接收到的十六進制加密字符串
* @param key 解密密鑰
* @return 解密後的明文字符串
*/
public String decryptFromHex(String hexData, String key) throws Exception {
// 1. Hex 字符串轉字節數組 (此步驟不涉及字符編碼)
byte[] encryptedBytes = hexStringToByteArray(hexData);
// 2. DES 解密得到原始字節數組
byte[] decryptedBytes = DESUtil.decrypt(encryptedBytes, key.getBytes("UTF-8"));
// 3. 關鍵:指定 UTF-8 將字節數組轉為字符串
return new String(decryptedBytes, "UTF-8");
}
/** Hex 字符串轉字節數組工具方法 */
private byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
| 場景 | 錯誤做法 | 正確做法 |
|---|---|---|
| 字符串獲取字節 | str.getBytes() (使用平台默認編碼) |
str.getBytes("UTF-8") |
| 字節轉字符串 | new String(bytes) |
new String(bytes, "UTF-8") |
| URL 參數傳遞 | 直接拼接中文或加密後的特殊字符 | 加密後轉 Hex/Base64 再 URLEncode |
// 1. 業務數據
String plainText = "用戶名:張三,訂單號:20231001";
// 2. 指定 UTF-8 獲取字節
byte[] data = plainText.getBytes("UTF-8");
// 3. 加密 (密鑰也用 UTF-8)
byte[] encrypted = DESUtil.encrypt(data, "MySecretKey".getBytes("UTF-8"));
// 4. 轉 Hex 字符串便於傳輸
String hexResult = DESUtil.bytesToHex(encrypted);
// 5. 返回給前端 (已設置 response charset=UTF-8)
return Result.success(hexResult);
進階建議:改用 Base64 編碼
Hex 編碼會使數據體積膨脹 2 倍,建議使用 Base64 編碼替代:
// 加密後編碼 String base64Result = Base64.getEncoder().encodeToString(encryptedBytes); // 解密前解碼 byte[] encryptedBytes = Base64.getDecoder().decode(base64String);優勢:體積只增加 33%、標準庫支持、URL 安全變體可直接傳參。
總結
- 統一編碼:全鏈路強制使用 UTF-8 (getBytes/ new String 都顯式指定)
- 傳輸編碼:加密字節數組必須轉為 Hex 或 Base64 字符串再傳輸
- HTTP 層面:請求/響應 Header 明確聲明 charset=UTF-8
- 密鑰處理:密鑰字符串獲取字節時同樣指定 UTF-8
遵循以上原則,可徹底消除 DES 加密中的中文亂碼問題。