MYSQL使用AES_ENCRYPT 加密数后JAVA确无法解密,原来问题是出行在了编码上

最近因为要做一个三级等保,需要将数据库中的敏感信息使用加密的方式保存。经过查询aes加密是比较方便的所以就对数据库数据进行了aes加密,使用的是mysql的AES_ENCRYPT函数。但是在程序写解密的时候就出行问题了,无论如何都无法正确解析密文。


/**
 * 隐私处理工具类
 */
public class PrivacyUtils {
 /**
 * 数据库AES加密的秘钥
 */
 private final static String aesKey = "脱敏处理的秘钥";

 private static final String KEY_ALGORITHM = "AES";
 private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法

 /**
 * 电话号码中间用*号代替显示
 *
 * @param mobile
 * @return
 */
 public static String secretMobile(String mobile) {
 if (StringUtils.isEmpty(mobile)) {
 return mobile;
 }
 String str = "";
 for (int i = 0; i < mobile.length(); i++) {
 if (i == mobile.length() - 11) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 10) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 9) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 3) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 2) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 1) {
 str += mobile.charAt(i);
 } else {
 str += "*";
 }


 }
 return str;
 }

 /**
 * 生成加密秘钥
 *
 * @return
 */
 private static SecretKeySpec getSecretKey(final String password) {
 //返回生成指定算法密钥生成器的 KeyGenerator 对象
 KeyGenerator kg = null;

 try {
 kg = KeyGenerator.getInstance(KEY_ALGORITHM);

 //AES 要求密钥长度为 128
 kg.init(128, new SecureRandom(password.getBytes()));

 //生成一个密钥
 SecretKey secretKey = kg.generateKey();

 return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥
 } catch (NoSuchAlgorithmException ex) {

 }

 return null;
 }

 /**
 * aes 加密处理,返回base64字符串
 * <p>
 * 1、aes 加密
 * 2、base64编码
 *
 * @return
 */
 public static String aesEncrypt(String string) throws Exception {
 if (string == null) {
 return null;
 }
 Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器

 byte[] byteContent = new byte[0];
 try {
 byteContent = string.getBytes("utf-8");
 } catch (UnsupportedEncodingException e) {
 e.printStackTrace();
 return string;
 }

 cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(aesKey));// 初始化为加密模式的密码器

 byte[] result = cipher.doFinal(byteContent);// 加密

 return Base64.encode(result);//通过Base64转码返回


 }

 /**
 * aes 解密处理,返回明文字符串
 *
 * @param string
 * @return
 */
 public static String aesDecrypt(String string) {
 if (StringUtils.isEmpty(string)) {
 return string;
 }
 try {
 //实例化
 Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

 //使用密钥初始化,设置为解密模式
 cipher.init(Cipher.DECRYPT_MODE, getSecretKey(aesKey));

 //执行操作
 byte[] result = cipher.doFinal(Base64.decode(string));

 return new String(result, "utf-8");
 } catch (Exception ex) {
 return string;
 }
 }
}

java 进行加密的数据能正常解密,但是mysql 加密处理后的密文确不能正确解密。加密的代码是公司以前留下的老代码。翻看了网上的帖子,写法也是这样的。没办法,只能继续找了。后面找到了这篇文章:https://blog.youkuaiyun.com/wojiaojianshang/article/details/90763206

发现不同之处在于获取SecretKeySpec 对象时,将秘钥转换车字节码指定的编码方式是“ASCII” ,而网上的大都是没有指定编码或者是指定为UTF-8编码,于是修改了代码实现,java可以正常加密和解密数据库的密文了。


/**
 * 隐私处理工具类
 */
public class PrivacyUtils {
 /**
 * 数据库AES加密的秘钥
 */
 private final static String aesKey = "脱敏处理的秘钥";


 private static final String KEY_ALGORITHM = "AES";
 private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法


 public static String getAesKey() {
 return aesKey;
 }

 /**
 * 电话号码中间用*号代替显示
 *
 * @param mobile
 * @return
 */
 public static String secretMobile(String mobile) {
 if (StringUtils.isEmpty(mobile)) {
 return mobile;
 }
 String str = "";
 for (int i = 0; i < mobile.length(); i++) {
 if (i == mobile.length() - 11) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 10) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 9) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 3) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 2) {
 str += mobile.charAt(i);
 } else if (i == mobile.length() - 1) {
 str += mobile.charAt(i);
 } else {
 str += "*";
 }


 }
 return str;
 }

 /**
 * 名字只显示第一个字
 * @param name
 * @return
 */
 public static String secretName(String name) {
 if (StringUtils.isEmpty(name)) {
 return name;
 }
 return name.charAt(0) + "***";
 }


 /**
 * aes 加密处理,返回base64字符串
 * <p>
 * 1、aes 加密
 * 2、base64编码
 *
 * @return
 */
 public static String aesEncrypt(String string) {
 try {
 SecretKey key = generateMySQLAESKey(string, "ASCII");
 Cipher cipher = Cipher.getInstance("AES");
 cipher.init(Cipher.ENCRYPT_MODE, key);
 byte[] cleartext = string.getBytes("UTF-8");
 byte[] ciphertextBytes = cipher.doFinal(cleartext);
 return Base64.encode(ciphertextBytes);

 } catch (Exception e) {
 e.printStackTrace();
 return string;
 }

 }

 /**
 * aes 解密处理,返回明文字符串
 *
 * @param string
 * @return
 */
 public static String aesDecrypt(String string) {
 if (StringUtils.isEmpty(string)) {
 return string;
 }

 try {
 SecretKey key = generateMySQLAESKey(aesKey, "ASCII");
 Cipher cipher = Cipher.getInstance("AES");
 cipher.init(Cipher.DECRYPT_MODE, key);
 byte[] cleartext = Base64.decode(string);
 byte[] ciphertextBytes = cipher.doFinal(cleartext);
 return new String(ciphertextBytes, "UTF-8");
 } catch (Exception e) {
 e.printStackTrace();
 return string;
 }

 }

 /**
 * 检查用户是否拥有某项权限
 *
 * @param authority 权限字符串编码
 * @return
 */
 public static Boolean checkHavePrincipal(String authority) {
 UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
 boolean secretMobile = false;
 if (userDetails != null) {
 Collection<? extends GrantedAuthority> list = userDetails.getAuthorities();
 for (GrantedAuthority grantedAuthority : list) {
 if (grantedAuthority.getAuthority().equals(authority)) {
 secretMobile = true;
 break;
 }
 }
 }
 return secretMobile;
 }

 /**
 * 生成mysql 加密使用的key
 *
 * @param key
 * @param encoding
 * @return
 */
 public static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) {
 try {
 final byte[] finalKey = new byte[16];
 int i = 0;
 for (byte b : key.getBytes(encoding))
 finalKey[i++ % 16] ^= b;
 return new SecretKeySpec(finalKey, "AES");
 } catch (UnsupportedEncodingException e) {
 throw new RuntimeException(e);
 }
 }


}

转载MYSQL使用AES_ENCRYPT 加密数后JAVA确无法解密,原来问题是出行在了编码上

<think>好的,我现在需要处理用户关于AES_ENCRYPT的具体实现和用法的问题。用户之前已经询问过SUBWORD和ROTWORD的含义,现在他们转向了实际应用中的函使用,特别是MySQL中的AES_ENCRYPT。根据提供的引用内容,用户可能想了如何在据库中使用这个函进行加密,以及背后的实现细节。 首先,我需要认用户的需求。他们提到“AES_encrypt的具体实现或用法”,但可能混淆了同系统中的函名称。例如,在MySQL中是AES_ENCRYPT,而其他编程语言可能有同的实现。根据用户提供的引用,大部分是关于MySQL的,所以应该重点讲MySQL中的使用。 接下来,我需要整理引用中的信息。引用1、2、3、5都提到了MySQLAES_ENCRYPTAES_DECRYPT函,强调它们的基本用法和参。引用4提到了GBase8c的类似函,可能说明用户对据库加密有更广泛的兴趣,但当前问题应集中在MySQL上。 用户之前的问题涉及AES的密钥扩展和底层操作,现在转向应用层面,可能需要连接理论到实际应用。例如,AESMySQL中的实现是否涉及SUBWORD和ROTWORD等步骤,或者如何配置加密如密钥长度和模式。 另外,用户提供的引用提到密钥长度、加密模式(如CBC)、初始化向量(IV)的使用,这些都需要在回答中涵盖。特别是引用4详细说明了IV的重要性,这可能需要在用法部分强调,尽管在MySQL的默认实现中可能显式使用IV,但用户需要知道如何正设置。 需要注意的是,用户可能对加密的安全性有疑问,比如如何选择合适的密钥,避免使用弱密钥,以及存储加密据的最佳实践。引用5提到SQL级别的加密,这可能涉及应用设计方面的考虑,如加密字段的选择和性能影响。 在结构上,我应该先介绍函的基本用法,包括语法和参,然后提供示例代码,如SQL语句。接着深入实现细节,比如MySQL使用AES模式和填充方式,密钥处理机制,可能涉及到的底层AES步骤(如SUBWORD和ROTWORD在密钥扩展中的作用)。然后讨论安全注意事项,如密钥管理和IV的使用。最后,与其他加密方法的对比和常见问题答,帮助用户全面理。 需要验证MySQLAES_ENCRYPT是否默认使用128位密钥,是否支持更长密钥,以及加密模式是否是ECB或CBC,因为同的模式会影响安全性和IV的必要性。根据引用2,MySQL可能默认使用128位,但允许扩展到256位,但需要认具体配置方式,如通过block_encryption_mode参设置。 另外,用户可能遇到的常见问题包括加密后的据存储(BLOB类型),编码问题(如字符集),以及如何在查询中使用加密字段进行检索(可能需要全文解密,影响性能)。 最后,结合用户之前的兴趣点(SUBWORD和ROTWORD),可以简要说明这些底层操作在MySQLAES实现中是如何被调用的,但可能需要深入代码层面,因为用户现在更关注应用。 总结回答结构:定义函、参、示例、实现细节(模式、填充、密钥处理)、安全建议、常见问题、相关函比较,以及相关问题。</think>### AES_ENCRYPT实现及用法详 --- #### 一、函定义与基本用法 **1. 函语法** ```sql AES_ENCRYPT(plaintext, key_str [, init_vector]) AES_DECRYPT(ciphertext, key_str [, init_vector]) ``` - **plaintext**:需加密的明文(字符串或二进制据) - **key_str**:加密密钥(长度建议与加密模式匹配,如128/192/256位) - **init_vector**:初始化向量(仅部分加密模式需要,如CBC)[^4] **2. 默认行为** - MySQL默认使用**AES-128-ECB**模式(无IV) - 可通过配置`block_encryption_mode`切换模式(如`aes-256-cbc`)[^4] - 加密结果以二进制字符串(BLOB类型)存储 --- #### 二、参与安全实践 **1. 密钥(key_str)** - **长度要求**: - ECB模式:密钥长度直接对应加密强度(128/192/256位) - CBC/CFB/OFB模式:由`block_encryption_mode`定义(如`aes-256-cfb`需256位密钥) - **安全建议**: - 使用随机生成的二进制密钥(如`SHA2(RAND(), 256)`) - 避免使用可预测的字符串(如生日、简单单词)[^5] **2. 初始化向量(init_vector)** - **适用模式**:CBC、CFB、OFB等需要IV的模式 - **生成规则**: - 长度≥16字节(超出部分自动截断) - CBC/CFB模式需保证**可预测性**(如随机生成) - OFB模式需保证**唯一性**(避免重复使用)[^4] --- #### 三、具体实现与底层逻辑 **1. 加密流程(以AES-128-CBC为例)** 1. **密钥扩展**: - 通过`SUBWORD`和`ROTWORD`生成轮密钥(参考用户前序问题) - 密钥长度由`block_encryption_mode`决定(如128位) 2. **分组加密**: - 明文按16字节分块,PKCS7填充(若长度足) - 每个块与前一个密文块异或(CBC模式) 3. **输出结果**: - 返回二进制格式密文(含填充字节) **2. 填充规则** - 默认使用**PKCS7填充**(如明文长度15字节 → 补1字节`0x01`) - 解密时自动去除填充 --- #### 四、代码示例与场景应用 **1. 基础加密/解密** ```sql -- 加密示例(ECB模式) INSERT INTO users (secret_data) VALUES (AES_ENCRYPT('信用卡号1234', 'my_secure_key_128')); -- 解密查询 SELECT AES_DECRYPT(secret_data, 'my_secure_key_128') FROM users WHERE id = 1; ``` **2. 使用CBC模式与IV** ```sql -- 设置加密模式 SET block_encryption_mode = 'aes-256-cbc'; -- 生成随机IV(16字节) SET @iv = RANDOM_BYTES(16); -- 加密(含IV) UPDATE payments SET encrypted_card = AES_ENCRYPT(card_number, '256bit_key', @iv) WHERE id = 100; ``` --- #### 五、安全注意事项 1. **密钥管理** - 禁止硬编码密钥(建议通过外部配置或KMS系统获取) - 定期轮换密钥(需重新加密历史据)[^5] 2. **模式选择** - 避免使用ECB模式(无法隐藏据模式) - 优先选择CBC(需随机IV)或GCM(支持认证加密) 3. **性能权衡** - 加密字段无法直接索引,需权衡查询效率 - 大据量加密建议结合列压缩[^5] --- #### 六、与其他加密的对比 | 函 | 算法 | 是否需要IV | 输出类型 | 典型场景 | |-----------------|--------|------------|---------------|------------------| | AES_ENCRYPT | AES | 可选 | 二进制字符串 | 结构化加密 | | MD5 | 哈希 | 无 | 十六进制字符串| 据指纹校验 | | SHA2 | 哈希 | 无 | 十六进制字符串| 密码存储 | --- ### 相关问题 1. 如何检测MySQLAES_ENCRYPT使用加密模式是否安全? 2. 在分布式系统中如何安全地同步AES加密的密钥和IV? 3. AES_ENCRYPT与OpenSSL命令行工具加密据如何互通? --- ### 引用标注 [^1]: 关于密钥一致性的说明 [^2]: AES密钥长度扩展机制 [^4]: IV在加密模式中的作用 [^5]: SQL级加密的应用场景与限制
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值