MySQL与Java实现AES-128-CBC无盐加密一致性问题求助
MySQL与Java AES-CBC加密一致性问题
我正在分别在MySQL和Java中实现AES加密,MySQL的加密脚本如下,其中AES_ENCRYPT函数不需要盐字节,密钥和IV是通过SHA2哈希后截取16位得到的:
SET session block_encryption_mode = 'aes-128-cbc'; SET @key_str = LEFT(UNHEX(SHA2('My secret passphrase',256)),16); AES_ENCRYPT(COLUMN_NAME,@key_str,LEFT(UNHEX(SHA2(IV,256)),16))
但用Java实现时,一开始用了PBEKeySpec(要求必须传盐),导致两边密文不一致,我的初始Java代码如下:
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; IvParameterSpec ivspec = new IvParameterSpec(iv); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 2, 128); SecretKey tmp = factory.generateSecret(spec); SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
解决方案
要让Java和MySQL生成相同密文,必须完全对齐两者的密钥、IV生成逻辑,以及加密规则,具体修改如下:
- 替换密钥生成逻辑
MySQL中密钥是直接对密码做SHA2-256哈希后取前16字节,不需要PBKDF2这种带盐的密钥派生算法,Java要按同样方式生成密钥:
// 生成和MySQL一致的AES-128密钥 MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); byte[] keyHash = sha256.digest("My secret passphrase".getBytes(StandardCharsets.UTF_8)); byte[] keyBytes = Arrays.copyOf(keyHash, 16); // 截取前16字节 SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
- 对齐IV生成逻辑
MySQL的IV是对指定字符串做SHA2-256哈希后取前16字节,Java也要遵循这个逻辑,不能用固定的全0数组:
// 生成和MySQL一致的IV byte[] ivHash = sha256.digest("你的IV字符串".getBytes(StandardCharsets.UTF_8)); byte[] ivBytes = Arrays.copyOf(ivHash, 16); IvParameterSpec ivspec = new IvParameterSpec(ivBytes);
确认加密模式与填充
MySQL的aes-128-cbc默认使用PKCS#7填充,这和Java的PKCS5Padding兼容(PKCS5是PKCS7的子集,针对8字节块),所以原代码中Cipher.getInstance("AES/CBC/PKCS5Padding")无需修改。完整可运行Java代码
import java.security.MessageDigest; import java.security.spec.IvParameterSpec; import java.security.spec.SecretKeySpec; import javax.crypto.Cipher; import java.util.Arrays; import java.nio.charset.StandardCharsets; public class AESEncryption { public static void main(String[] args) throws Exception { String plaintext = "需要加密的内容"; String passphrase = "My secret passphrase"; String ivInput = "你的IV字符串"; // 生成密钥 MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); byte[] keyHash = sha256.digest(passphrase.getBytes(StandardCharsets.UTF_8)); byte[] keyBytes = Arrays.copyOf(keyHash, 16); SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); // 生成IV byte[] ivHash = sha256.digest(ivInput.getBytes(StandardCharsets.UTF_8)); byte[] ivBytes = Arrays.copyOf(ivHash, 16); IvParameterSpec ivspec = new IvParameterSpec(ivBytes); // 执行加密 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec); byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); // 转成MySQL兼容的十六进制字符串(可选) String hexCiphertext = bytesToHex(ciphertext); System.out.println(hexCiphertext); } // 辅助方法:字节数组转十六进制 private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } }
内容的提问来源于stack exchange,提问作者Muddassir Rahman
相关产品推荐
相关产品推荐

