关于使用python/php实现mysql aes_encrypt(str,pass)超长密文加密解解决方案

开发环境:

mysql 5.7

python 3.8

php 7

centos7.9

 

使用 mysql的aes_encrypt 进行数据加密,使用 hex(aes_encrypt(原文, 密钥)) 加密生成密文,并使用 aes_decrypt(unhex(密文), 密钥) 进行解密。

--注意,当密钥长度超过16位时,mysql会对密钥进行处理,生成16位长度的新密钥。这点需要特别注意。这也是php python实现方法中“模拟mysql的密钥生成逻辑”存在的目的
--例如 密钥='fdsar23sv3qrgasdfasdfh34gsdv' 该密钥在php 或者python实现中,不可简单的截取前16位,而是要模拟 mysql 越长密钥的生成逻辑进行处理
-- 加密
SELECT HEX(AES_ENCRYPT('0123456789', '密钥'));

-- 解密
SELECT AES_DECRYPT(HEX('密文'), '密钥');

 

下面是使用php进行该功能的实现

 1 function mysql_aes_key($passphrase) {// 模拟MySQL的密钥生成逻辑(循环异或处理超长密钥)
 2     $key = str_repeat("\0", 16); // 初始化16字节缓冲区
 3     $passBytes = $passphrase;    // 原始密钥字节流
 4     for ($i = 0; $i < strlen($passBytes); $i++) {
 5         $key[$i % 16] = $key[$i % 16] ^ $passBytes[$i]; // 循环异或
 6     }
 7     return $key;
 8 }
 9 function mysql_aes_decrypt($hex_data,$passphrase){
10     try{
11         if(!ctype_xdigit($hex_data) || strlen($hex_data)%2!=0){
12             throw new Exception('Invalid hex data');
13         }
14         // 将十六进制表示转换为二进制数据
15         $encrypted_binary= hex2bin($hex_data);
16         // 2. 处理密钥
17         $key = mysql_aes_key($passphrase);
18         // 使用openssl_decrypt函数解密数据
19         // 加密算法为AES-128-ECB,这是MySQL中默认的AES加密算法
20         // 指定了OPENSSL_RAW_DATA选项,以确保解密后的数据不进行任何格式转换
21         $decrypted_data = openssl_decrypt($encrypted_binary,'AES-128-ECB',$key,OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
22         
23         // 4. 安全去除PKCS7填充(PHP需手动处理)
24         $pad = ord($decrypted_data[strlen($decrypted_data) - 1]);
25         $unpadded_data = substr($decrypted_data, 0, -$pad);
26 
27         // 5. 编码转换与过滤(兼容Python的decode('utf-8'))
28         $decrypted_data = mb_convert_encoding($unpadded_data, 'UTF-8', 'UTF-8,GBK,ISO-8859-1');
29         // $decrypted_data = iconv('UTF-8', 'UTF-8//IGNORE', $decrypted_data);
30         return $decrypted_data;
31     } catch (Exception $e) {
32         error_log("Decryption failed: " . $e->getMessage());
33         return $hex_data;
34     } catch (Throwable $t) {
35         // 处理二进制解码异常
36         error_log("Unicode error: " . $t->getMessage());
37         return '';
38     }
39 }
40 /** 
41  * mysql HEX(AES_ENCRYPT('原文','密钥')) 
42  * @param string $data 要加密的数据
43  * @param string $passhprase 加密密钥
44  * @param string $cipher_algo 加密算法
45  * @param int $options 加密选项
46  * @return string 加密后的十六进制字符串
47  */
48 function mysql_aes_encrypt($data,$passphrase){
49     try{
50 
51         // 使用openssl_encrypt函数进行AES加密
52         // 加密算法为AES-128-ECB,这是MySQL中默认的AES加密算法
53         // 指定了OPENSSL_RAW_DATA选项,以确保加密后的数据不进行任何格式转换
54         // $encrypted = openssl_encrypt($data,$cipher_algo,$passphrase,$options);
55         
56         // 模拟 mysql 处理超长密钥
57         $key = mysql_aes_key($passphrase);
58         $encrypted = openssl_encrypt($data,'AES-128-ECB',$key,OPENSSL_RAW_DATA);
59         
60         // 将加密结果转换为十六进制字符串
61         $hex_encrypted= bin2hex($encrypted);
62         return $hex_encrypted;
63     }catch(\Exception $e){
64         return $data;
65     }
66 }

下面是使用python进行实现:

 1 from Crypto.Cipher import AES
 2 from Crypto.Util.Padding import pad, unpad
 3 
 4 
 5 # import hashlib
 6 
 7 def mysql_aes_key(passphrase: str) -> bytes:
 8     """模拟MySQL的密钥生成逻辑(循环异或处理超长密钥)"""
 9     key = bytearray(16)  # 初始化16字节缓冲区
10     pass_bytes = passphrase.encode('utf-8')
11     for i in range(len(pass_bytes)):
12         key[i % 16] ^= pass_bytes[i]  # 循环异或操作
13     return bytes(key)
14 
15 def mysql_aes_encrypt(data: str, passphrase: str) -> str:
16     """
17     字符串加密
18     :param data: 待加密字符串
19     :param passphrase: 密钥
20     """
21     try:
22         # 1. 密钥处理:MD5哈希生成16字节密钥(MySQL兼容性)
23         # key = hashlib.md5(passphrase.encode()).digest()
24         # key = passphrase.encode('utf-8')[:16].ljust(16, b'\0')  # 直接截取前16字节,不足补零
25         key = mysql_aes_key(passphrase)
26 
27         # 2. PKCS7填充(PHP/MySQL默认填充方式)
28         padded_data = pad(data.encode('utf-8'), AES.block_size, style='pkcs7')
29 
30         # 3. AES-128-ECB加密(无IV,兼容MySQL)
31         cipher = AES.new(key, AES.MODE_ECB)
32         encrypted_data = cipher.encrypt(padded_data)
33 
34         # 4. 转为HEX字符串(大写,兼容MySQL的HEX())
35         return encrypted_data.hex().upper()
36     except Exception as e:
37         # 异常时返回原始数据(与PHP行为一致)
38         return data
39 
40 
41 def mysql_aes_decrypt(hex_data: str, passphrase: str) -> str:
42     """
43     字符串加密
44     :param hex_data: 待解密字符串
45     :param passphrase: 密钥
46     """
47     try:
48         # 1. 检查HEX格式并转换
49         encrypted_data = bytes.fromhex(hex_data)
50 
51         # 2. 生成MD5哈希密钥(16字节)
52         # key = hashlib.md5(passphrase.encode()).digest()
53         # key = passphrase.encode('utf-8')[:16].ljust(16, b'\0')  # 直接截取前16字节,不足补零
54         key = mysql_aes_key(passphrase)
55 
56         # 3. ECB模式解密
57         cipher = AES.new(key, AES.MODE_ECB)
58         decrypted_data = cipher.decrypt(encrypted_data)
59 
60         # 4. 安全去除PKCS7填充
61         unpadded_data = unpad(decrypted_data, AES.block_size, style='pkcs7')
62 
63         return unpadded_data.decode('utf-8')
64     except (ValueError, TypeError) as e:
65         print(f"解密失败: {str(e)}")
66         return hex_data  # 或抛出异常
67     except UnicodeDecodeError:
68         # return "解密成功但编码异常"  # 处理非文本数据
69         print("解密成功但编码异常")
70         return ''

 

posted @ 2025-04-03 17:27  动灵  阅读(61)  评论(0)    收藏  举报