首頁  >  文章  >  資料庫  >  MySQL如何正確地利用AES_ENCRYPT()與AES_DECRYPT()加解密

MySQL如何正確地利用AES_ENCRYPT()與AES_DECRYPT()加解密

小云云
小云云原創
2018-05-18 17:40:3717308瀏覽

本文主要為大家介紹了關於MySQL利用AES_ENCRYPT()與AES_DECRYPT()加解密的正確方法,MySQL中AES_ENCRYPT('密碼','鑰匙')函數可以對字段值做加密處理,AES_DECRYPT(表的字段名字,'鑰匙')函數解密處理,文中給了詳細的範例程式碼,需要的朋友可以參考下,希望能幫助大家。

前言

最近在工作中遇到需求是這樣的:需要在使用AES_ENCRYPT()函數將明文加密,儲存在MySQL中,但遇到了一些問題…以下就來詳細介紹下。

說將加密後的密文,解密取出是NULL。

看了一下,她發過來的表結構:

#再看了她透過AES_DECRYPT()函數加密了一個字串,然後insert進去了,執行成功後,顯示了一個warning:<br>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: &#39;\xE3f767\x12...&#39; for column &#39;passwd&#39; at row 1 |
+---------+------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

查了一下文檔,看一下這兩個函數的使用:

-- 将&#39;hello world&#39;加密,密钥为&#39;key&#39;,加密后的串存在@pass中
mysql> SET @pass=AES_ENCRYPT(&#39;hello world&#39;, &#39;key&#39;); 
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, &#39;key&#39;);
+---------------------------+
| AES_DECRYPT(@pass, &#39;key&#39;) |
+---------------------------+
| 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(&#39;明文1&#39;, &#39;key&#39;), AES_ENCRYPT(&#39;text2&#39;, &#39;key&#39;), AES_ENCRYPT(&#39;明文_text3&#39;, &#39;key&#39;)); 
Query OK, 1 row affected (0.01 sec)

mysql> SELECT AES_DECRYPT(pass1, &#39;key&#39;), AES_DECRYPT(pass2, &#39;key&#39;), AES_DECRYPT(pass3, &#39;key&#39;) FROM t_passwd;
+---------------------------+---------------------------+---------------------------+
| AES_DECRYPT(pass1, &#39;key&#39;) | AES_DECRYPT(pass2, &#39;key&#39;) | AES_DECRYPT(pass3, &#39;key&#39;) |
+---------------------------+---------------------------+---------------------------+
| 明文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(&#39;hello world&#39;, &#39;key2&#39;)));
Query OK, 1 row affected (0.00 sec)

mysql> SELECT AES_DECRYPT(UNHEX(pass1), &#39;key2&#39;) FROM t_passwd_2; 
+-----------------------------------+
| AES_DECRYPT(UNHEX(pass1), &#39;key2&#39;) |
+-----------------------------------+
| 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(&#39;text&#39;, &#39;key3&#39;);
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0

mysql> SELECT AES_DECRYPT(pass, &#39;key3&#39;) FROM t_passwd_3;
+---------------------------+
| AES_DECRYPT(pass, &#39;key3&#39;) |
+---------------------------+
| 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 VARBIRYRY or OB bin em. 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中,那就還是參考方法②,十六進位化一下吧。


或如同方法①,直接存在二進位欄位中。

相關推薦:


在PHP中如何利用OpenSSL取代Mcrypt加解密?

微信小程式開發功能介紹之加解密NODE-UUID介紹

詳解PHP資料壓縮、加上解密(pack, unpack)

以上是MySQL如何正確地利用AES_ENCRYPT()與AES_DECRYPT()加解密的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn