MySQL使用AES_ENCRYPT()/AES_DECRYPT()加解密的正確姿勢
遇到一個需求是這樣的:
需要在使用AES_ENCRYPT()函式將明文加密,儲存在MySQL中,但是遇到了一些問題……
說將加密後的密文,解密取出來是NULL。
看了一下,她發過來的表結構:
再看了她透過AES_DECRYPT()函式加密了一個字串,然後insert進去了,執行成功後,顯示了一個warning:
Query OK, 1 row affected, 1 warning (0.00 sec)
(沒有報錯而是warning,大概是sql_mode的緣故)
此時她忽略了這個warning,再透過AES_DECRYPT()解密後,發現取出來的明文為NULL。
再回看錶結構,發現其欄位屬性為“varchar” && 字符集是ut8,檢查warning為下:
查了一下文件,看一下這兩個函式的使用:
那麼到底該如何存呢?
方法①:
將欄位屬性設定為varbinary/binary/四個blob型別,等二進位制欄位屬性。
建立三個欄位,屬性分別為varbinary、binary、blob。
並將'明文1','text2','明文_text3'加密,金鑰為key,存入表中。
最後取出。
當然,屬性括號內的長度要取決於明文的長度,此處明文較短,故只給了16。
方法②:
將密文十六進位制化,再存入varchar/char列。
此處需要用到HEX()來存入,用UNHEX()取出。
建立一個字串屬性的欄位。
將'hello world'先用金鑰'key2'進行AES加密,再將加密後的串透過HEX函式十六進位制化。
最後先將加密後的串透過UNHEX取出,再透過AES據金鑰'key2'解密:
同樣,根據明文的長度不同,AES_ENCRYPT加密後的串長度也會有所變化,所以HEX後的字串長度也會有所變化。
實際使用時,需要據業務評估出一個合理值即可。
方法③:
直接存入varchar中,不做十六進位制化。
回溯到問題的一開始,將加密後的串,存到utf8字符集並且屬性為varchar中,是不行的。
實際上,將字符集改成latin1就可以了:
在insert的時候也不會報warning了。
這樣的方法雖然美,只需將欄位字符集設定為latin1就可以了,但可能會帶來隱患:
文件上寫了這樣的一句:
大意是,如果用方法③那樣,直接將加密後的串存入char/varchar/text型別中,在做字元轉換的時或空格被刪除時,可能會帶來潛在的影響。
所以如果一定要存在char/varchar/text中,那麼還是參考方法②,十六進位制化一下吧。
或者如同方法①,直接存在二進位制欄位中。
參考文件:
Chapter 12 Functions and Operators - 12.13 Encryption and Compression Functions
需要在使用AES_ENCRYPT()函式將明文加密,儲存在MySQL中,但是遇到了一些問題……
說將加密後的密文,解密取出來是NULL。
看了一下,她發過來的表結構:
再看了她透過AES_DECRYPT()函式加密了一個字串,然後insert進去了,執行成功後,顯示了一個warning:
Query OK, 1 row affected, 1 warning (0.00 sec)
(沒有報錯而是warning,大概是sql_mode的緣故)
此時她忽略了這個warning,再透過AES_DECRYPT()解密後,發現取出來的明文為NULL。
再回看錶結構,發現其欄位屬性為“varchar” && 字符集是ut8,檢查warning為下:
-
mysql> show warnings;
-
+---------+------+------------------------------------------------------------------------+
-
| Level | Code | Message |
-
+---------+------+------------------------------------------------------------------------+
-
| Warning | 1366 | Incorrect string value: '\xE3f767\x12...' for column 'passwd' at row 1 |
-
+---------+------+------------------------------------------------------------------------+
- 1 row in set (0.00 sec)
查了一下文件,看一下這兩個函式的使用:
-
-- 將'hello world'加密,金鑰為'key',加密後的串存在@pass中
-
mysql> SET @pass=AES_ENCRYPT('hello world', 'key');
-
Query OK, 0 rows affected (0.00 sec)
-
-
-- 看一下加密後串的長度(都為2的整數次方)
-
mysql> SELECT CHAR_LENGTH(@pass);
-
+--------------------+
-
| CHAR_LENGTH(@pass) |
-
+--------------------+
-
| 16 |
-
+--------------------+
-
1 row in set (0.00 sec)
-
-
-- 使用AES_DECRYPT()解密
-
mysql> SELECT AES_DECRYPT(@pass, 'key');
-
+---------------------------+
-
| AES_DECRYPT(@pass, 'key') |
-
+---------------------------+
-
| hello world |
-
+---------------------------+
- 1 row in set (0.00 sec)
那麼到底該如何存呢?
方法①:
將欄位屬性設定為varbinary/binary/四個blob型別,等二進位制欄位屬性。
建立三個欄位,屬性分別為varbinary、binary、blob。
並將'明文1','text2','明文_text3'加密,金鑰為key,存入表中。
最後取出。
-
mysql> CREATE TABLE t_passwd (pass1 varbinary(16), pass2 binary(16), pass3 blob);
- Query OK, 0 rows affected (0.00 sec)
-
-
mysql> INSERT INTO t_passwd VALUES (AES_ENCRYPT('明文1', 'key'), AES_ENCRYPT('text2', 'key'), AES_ENCRYPT('明文_text3', 'key'));
-
Query OK, 1 row affected (0.01 sec)
-
-
mysql> SELECT AES_DECRYPT(pass1, 'key'), AES_DECRYPT(pass2, 'key'), AES_DECRYPT(pass3, 'key') FROM t_passwd;
-
+---------------------------+---------------------------+---------------------------+
-
| AES_DECRYPT(pass1, 'key') | AES_DECRYPT(pass2, 'key') | AES_DECRYPT(pass3, 'key') |
-
+---------------------------+---------------------------+---------------------------+
-
| 明文1 | text2 | 明文_text3 |
-
+---------------------------+---------------------------+---------------------------+
- 1 row in set (0.00 sec)
當然,屬性括號內的長度要取決於明文的長度,此處明文較短,故只給了16。
方法②:
將密文十六進位制化,再存入varchar/char列。
此處需要用到HEX()來存入,用UNHEX()取出。
建立一個字串屬性的欄位。
將'hello world'先用金鑰'key2'進行AES加密,再將加密後的串透過HEX函式十六進位制化。
最後先將加密後的串透過UNHEX取出,再透過AES據金鑰'key2'解密:
-
mysql> CREATE TABLE t_passwd_2(pass1 char(32));
-
Query OK, 0 rows affected (0.01 sec)
-
-
mysql> INSERT INTO t_passwd_2 VALUES (HEX(AES_ENCRYPT('hello world', 'key2')));
-
Query OK, 1 row affected (0.00 sec)
-
-
mysql> SELECT AES_DECRYPT(UNHEX(pass1), 'key2') FROM t_passwd_2;
-
+-----------------------------------+
-
| AES_DECRYPT(UNHEX(pass1), 'key2') |
-
+-----------------------------------+
-
| hello world |
-
+-----------------------------------+
- 1 row in set (0.00 sec)
同樣,根據明文的長度不同,AES_ENCRYPT加密後的串長度也會有所變化,所以HEX後的字串長度也會有所變化。
實際使用時,需要據業務評估出一個合理值即可。
方法③:
直接存入varchar中,不做十六進位制化。
回溯到問題的一開始,將加密後的串,存到utf8字符集並且屬性為varchar中,是不行的。
實際上,將字符集改成latin1就可以了:
在insert的時候也不會報warning了。
-
mysql> CREATE TABLE t_passwd_3(pass varchar(32)) CHARSET latin1;
-
Query OK, 0 rows affected (0.00 sec)
-
-
mysql> INSERT INTO t_passwd_3 SELECT AES_ENCRYPT('text', 'key3');
-
Query OK, 1 row affected (0.00 sec)
-
Records: 1 Duplicates: 0 Warnings: 0
-
-
mysql> SELECT AES_DECRYPT(pass, 'key3') FROM t_passwd_3;
-
+---------------------------+
-
| AES_DECRYPT(pass, 'key3') |
-
+---------------------------+
-
| text |
-
+---------------------------+
- 1 row in set (0.00 sec)
這樣的方法雖然美,只需將欄位字符集設定為latin1就可以了,但可能會帶來隱患:
文件上寫了這樣的一句:
- Many encryption and compression functions return strings for which the result might contain arbitrary byte values. If you want to store these results, use a column with a VARBINARY or BLOB binary string data type. This will avoid potential problems with trailing space removal or character set conversion that would change data values, such as may occur if you use a nonbinary string data type (CHAR, VARCHAR, TEXT).
大意是,如果用方法③那樣,直接將加密後的串存入char/varchar/text型別中,在做字元轉換的時或空格被刪除時,可能會帶來潛在的影響。
所以如果一定要存在char/varchar/text中,那麼還是參考方法②,十六進位制化一下吧。
或者如同方法①,直接存在二進位制欄位中。
參考文件:
Chapter 12 Functions and Operators - 12.13 Encryption and Compression Functions
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29773961/viewspace-2142305/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Redis的正確使用姿勢Redis
- Postman 正確使用姿勢Postman
- MySQL 5.6建索引的正確姿勢MySql索引
- Python 操作 MySQL 的正確姿勢PythonMySql
- laravel 使用 es 的正確姿勢Laravel
- 使用快取的正確姿勢快取
- 原始碼|使用FutureTask的正確姿勢原始碼
- npm run dev 的正確使用姿勢NPMdev
- 使用 Java 8 Optional 的正確姿勢Java
- Java日誌正確使用姿勢Java
- Spring Boot使用AOP的正確姿勢Spring Boot
- 使用 react Context API 的正確姿勢ReactContextAPI
- 模組開發者使用 ES Modules 的正確姿勢
- Python re 庫的正確使用姿勢Python
- Fragment全解析(2):正確的使用姿勢Fragment
- 中國菜刀使用(實戰正確姿勢)
- GIT使用rebase和merge的正確姿勢Git
- Swift中使用Contains的正確姿勢SwiftAI
- Flexbox 佈局的正確使用姿勢Flex
- Laravel 消費佇列的正確使用姿勢Laravel佇列
- [小卓筆記]:使用Storyboard的正確姿勢筆記
- 在Windows下使用vim grep的正確姿勢Windows
- Android 執行緒的正確使用姿勢Android執行緒
- TCP三次握手的正確使用姿勢TCP
- git commit 的正確姿勢GitMIT
- 玩轉 Ceph 的正確姿勢
- 開啟Git的正確姿勢Git
- java關流的正確姿勢Java
- Fragment commit 的正確姿勢FragmentMIT
- 解讀mysql的索引和事務的正確姿勢MySql索引
- 【通俗易懂】JWT-使用的可能正確姿勢JWT
- “5Why分析法”的正確使用姿勢
- 在 Laravel Mix 裡使用 Vux 2 的正確姿勢LaravelUX
- 相容iphone x劉海的正確姿勢iPhone
- 限制UITextField字數的正確姿勢UI
- 解鎖 Redis 鎖的正確姿勢Redis
- 解鎖redis鎖的正確姿勢Redis
- 演算法分析的正確姿勢演算法