12.13 加密和压缩功能

table12.17 加密功能

NameDescription
AES_DECRYPT()使用 AES 解密
AES_ENCRYPT()使用 AES 加密
ASYMMETRIC_DECRYPT()使用私钥或公钥解密密文
ASYMMETRIC_DERIVE()从非对称密钥派生对称密钥
ASYMMETRIC_ENCRYPT()使用私钥或公钥加密明文
ASYMMETRIC_SIGN()从摘要生成签名
ASYMMETRIC_VERIFY()验证签名匹配摘要
COMPRESS()以二进制字符串形式返回结果
CREATE_ASYMMETRIC_PRIV_KEY()创建私钥
CREATE_ASYMMETRIC_PUB_KEY()创建公钥
CREATE_DH_PARAMETERS()生成共享的 DH 机密
CREATE_DIGEST()从字符串生成摘要
DECODE() (deprecated)解码使用 ENCODE()加密的字符串
DES_DECRYPT() (deprecated)解密字符串
DES_ENCRYPT() (deprecated)加密字符串
ENCODE() (deprecated)编码字符串
ENCRYPT() (deprecated)加密字符串
MD5()计算 MD5 校验和
PASSWORD() (deprecated)计算并返回密码字符串
RANDOM_BYTES()返回一个随机字节向量
SHA1(), SHA()计算 SHA-1 160 位校验和
SHA2()计算 SHA-2 校验和
UNCOMPRESS()解压字符串压缩
UNCOMPRESSED_LENGTH()返回压缩前的字符串长度
VALIDATE_PASSWORD_STRENGTH()确定密码强度

许多加密和压缩函数返回字符串,其结果可能包含任意字节值。如果要存储这些结果,请使用具有VARBINARYBLOB二进制字符串数据类型的列。这将避免在行尾空格删除或字符集转换中可能会更改数据值的潜在问题,例如,如果您使用非二进制字符串数据类型(CHARVARCHARTEXT),则可能会发生问题。

一些加密函数返回 ASCII 字符的字符串:MD5()PASSWORD()SHA()SHA1()SHA2()。它们的返回值是一个字符串,该字符串具有由character_set_connectioncollation_connection系统变量确定的字符集和排序规则。除非字符集是binary,否则这是一个非二进制字符串。

如果应用程序存储了诸如MD5()SHA1()之类的函数的返回十六进制数字字符串的值,则可以通过使用UNHEX()将十六进制 table 示形式转换为二进制并将结果存储在BINARY(N)列中来获得更有效的存储和比较。每对十六进制数字都需要一个二进制形式的字节,因此* N *的值取决于十六进制字符串的长度。 * N 对于MD5()值是 16,对于SHA1()值是 20.对于SHA2() N *的范围从 28 到 32,具体取决于指定结果所需位长的参数。

将十六进制字符串存储在CHAR列中的大小损失至少为两倍,如果将值存储在使用utf8字符集的列中(每个字符使用 4 个字节),则该损失最多为八倍。由于较大的值以及需要考虑字符集排序规则,因此存储字符串还会导致比较速度变慢。

假设应用程序将MD5()字符串值存储在CHAR(32)列中:

CREATE TABLE md5_tbl (md5_val CHAR(32), ...);
INSERT INTO md5_tbl (md5_val, ...) VALUES(MD5('abcdef'), ...);

要将十六进制字符串转换为更紧凑的形式,请修改应用程序以使用UNHEX()BINARY(16)代替,如下所示:

CREATE TABLE md5_tbl (md5_val BINARY(16), ...);
INSERT INTO md5_tbl (md5_val, ...) VALUES(UNHEX(MD5('abcdef')), ...);

应用程序应该准备好处理非常罕见的情况,即哈希函数为两个不同的 Importing 值生成相同的值。使冲突可检测的一种方法是使哈希列成为主键。

Note

MD5 和 SHA-1 算法的利用已广为人知。您可能希望考虑使用本节中介绍的另一种单向加密功能,例如SHA2()

Caution

除非使用 SSL 连接,否则将作为加密函数的参数提供的密码或其他敏感值作为明文发送到 MySQL 服务器。同样,这些值将出现在写入它们的任何 MySQL 日志中。为了避免这些类型的暴露,应用程序可以在将敏感值发送到服务器之前在 Client 端上对其进行加密。相同的注意事项适用于加密密钥。为了避免暴露这些情况,应用程序可以使用存储过程在服务器端对值进行加密和解密。

AES_DECRYPT(crypt_str,key_str[,init_vector])

此功能使用官方的 AES(高级加密标准)算法解密数据。有关更多信息,请参见AES_ENCRYPT()的描述。

使用AES_DECRYPT()的语句对于基于语句的复制是不安全的,因此无法存储在查询缓存中。

AES_ENCRYPT(str,key_str[,init_vector])

AES_ENCRYPT()AES_DECRYPT()使用官方的 AES(高级加密标准)算法(以前称为“ Rijndael”)实现数据的加密和解密。 AES 标准允许各种密钥长度。默认情况下,这些功能使用 128 位密钥长度实现 AES。可以使用 196 或 256 位的密钥长度,如下所述。密钥长度是性能和安全性之间的折衷。

AES_ENCRYPT()使用密钥字符串key_str *对字符串str进行加密,并返回包含加密输出的二进制字符串。 AES_DECRYPT()使用密钥字符串_key_str 解密加密的字符串 crypt_str *并返回原始的纯文本字符串。如果任何一个函数参数均为NULL,则函数返回NULL

  • str crypt_str 参数可以是任意长度,并且填充会自动添加到 str *,因此它是基于块的算法(例如 AES)所要求的块的倍数。 AES_DECRYPT()功能会自动删除此填充。 * crypt_str *的长度可以使用以下公式计算:
16 * (trunc(string_length / 16) + 1)

对于 128 位的密钥长度,将密钥传递给* key_str *参数的最安全方法是创建一个 true 随机的 128 位值并将其作为二进制值传递。例如:

INSERT INTO t
VALUES (1,AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3')));

通过对密码短语进行哈希处理,可以使用密码短语生成 AES 密钥。例如:

INSERT INTO t
VALUES (1,AES_ENCRYPT('text', UNHEX(SHA2('My secret passphrase',512))));

不要将密码或密码直接传递给* crypt_str *,请先对其进行哈希处理。本文档的早期版本建议采用以前的方法,但由于此处显示的示例更加安全,因此不再建议使用此方法。

如果AES_DECRYPT()检测到无效数据或不正确的填充,则返回NULL。但是,如果 Importing 数据或键无效,则AES_DECRYPT()可能返回非NULL值(可能是垃圾)。

AES_ENCRYPT()AES_DECRYPT()允许控制块加密模式,并采用可选的* init_vector *初始化矢量参数:

  • block_encryption_mode系统变量控制基于块的加密算法的模式。其默认值为aes-128-ecb,table 示使用 128 位密钥长度和 ECB 模式进行加密。有关此变量的允许值的说明,请参见第 5.1.7 节“服务器系统变量”

    • 可选的* init_vector *参数为需要它的块加密模式提供初始化向量。

对于需要可选* init_vector 参数的模式,它必须为 16 个字节或更长(大于 16 的字节将被忽略)。如果 init_vector *丢失,则会发生错误。

对于不需要* init_vector *的模式,将忽略它,并且如果指定了警告,则会生成警告。

可以通过调用RANDOM_BYTES(16)生成用于初始化向量的随机字节字符串。对于需要初始化向量的加密模式,必须将相同的向量用于加密和解密。

mysql> SET block_encryption_mode = 'aes-256-cbc';
mysql> SET @key_str = SHA2('My secret passphrase',512);
mysql> SET @init_vector = RANDOM_BYTES(16);
mysql> SET @crypt_str = AES_ENCRYPT('text',@key_str,@init_vector);
mysql> SELECT AES_DECRYPT(@crypt_str,@key_str,@init_vector);
+-----------------------------------------------+
| AES_DECRYPT(@crypt_str,@key_str,@init_vector) |
+-----------------------------------------------+
| text                                          |
+-----------------------------------------------+

下 table 列出了每种允许的块加密模式,支持该模式的 SSL 库以及是否需要初始化矢量参数。

块加密模式支持模式的 SSL 库需要初始化向量
ECBOpenSSL, yaSSLNo
CBCOpenSSL, yaSSLYes
CFB1OpenSSLYes
CFB8OpenSSLYes
CFB128OpenSSLYes
OFBOpenSSLYes

使用AES_ENCRYPT()AES_DECRYPT()的语句对于基于语句的复制是不安全的,并且不能存储在查询缓存中。

COMPRESS(string_to_compress)

压缩字符串并将结果作为二进制字符串返回。此函数要求 MySQL 已使用诸如zlib之类的压缩库进行编译。否则,返回值始终为NULL。压缩的字符串可以使用UNCOMPRESS()解压缩。

mysql> SELECT LENGTH(COMPRESS(REPEAT('a',1000)));
        -> 21
mysql> SELECT LENGTH(COMPRESS(''));
        -> 0
mysql> SELECT LENGTH(COMPRESS('a'));
        -> 13
mysql> SELECT LENGTH(COMPRESS(REPEAT('a',16)));
        -> 15

压缩的字符串内容按以下方式存储:

  • 空字符串存储为空字符串。

    • 非空字符串存储为未压缩字符串的 4 字节长度(低字节在前),然后是压缩字符串。如果字符串以空格结尾,则应添加一个额外的.字符,以免在将结果存储在CHARVARCHAR列中的情况下进行尾部修剪。 (但是,无论如何,不建议使用CHARVARCHAR之类的非二进制字符串数据类型来存储压缩字符串,因为可能会发生字符集转换。请改用VARBINARYBLOB二进制字符串列。)

DECODE(crypt_str,pass_str)

DECODE()使用* pass_str 作为密码解密加密的字符串 crypt_str *。 * crypt_str *应该是从ENCODE()返回的字符串。

Note

ENCODE()DECODE()函数在 MySQL 5.7 中已弃用,将在以后的 MySQL 版本中删除,并且不再使用。考虑改用AES_ENCRYPT()AES_DECRYPT()

DES_DECRYPT(crypt_str[,key_str])

解密使用DES_ENCRYPT()加密的字符串。如果发生错误,此函数将返回NULL

仅当 MySQL 已配置有 SSL 支持时,此功能才有效。参见第 6.3 节“使用加密的连接”

如果没有给出* key_str *参数,则DES_DECRYPT()检查加密字符串的第一个字节,以确定用于加密原始字符串的 DES 密钥号,然后从 DES 密钥文件中读取密钥以解密消息。为此,用户必须具有SUPER特权。可以使用--des-key-file服务器选项指定密钥文件。

如果为该函数传递* key_str *参数,则该字符串将用作解密消息的密钥。

如果* crypt_str 参数似乎不是加密的字符串,则 MySQL 返回给定的 crypt_str *。

Note

从 MySQL 5.7.6 开始不推荐使用DES_ENCRYPT()DES_DECRYPT()函数,这些函数将在以后的 MySQL 版本中删除,并且不再使用。考虑改用AES_ENCRYPT()AES_DECRYPT()

DES_ENCRYPT(str[,{key_num|key_str}])

使用 Triple-DES 算法使用给定的密钥对字符串进行加密。

仅当 MySQL 已配置有 SSL 支持时,此功能才有效。参见第 6.3 节“使用加密的连接”

如果给定了DES_ENCRYPT()的第二个参数,则选择要使用的加密密钥。不带参数的情况下,将使用 DES 密钥文件中的第一个密钥。通过* key_num 参数,使用 DES 密钥文件中给定的密钥号(0 到 9)。使用 key_str 参数,给定的密钥字符串用于加密 str *。

密钥文件可以使用--des-key-file服务器选项指定。

返回字符串是一个二进制字符串,其中第一个字符为CHAR(128 | key_num)。如果发生错误,则DES_ENCRYPT()返回NULL

添加了 128,以便更轻松地识别加密密钥。如果您使用字符串键,则* key_num *为 127.

结果的字符串长度由以下公式给出:

new_len = orig_len + (8 - (orig_len % 8)) + 1

DES 密钥文件中的每一行具有以下格式:

key_num des_key_str

每个* key_num *值必须是09范围内的数字。文件中的行可以按任何 Sequences 排列。 * des_key_str *是用于加密消息的字符串。数字和键之间至少应有一个空格。如果您没有为DES_ENCRYPT()指定任何 key 参数,则第一个键是默认键。

您可以使用FLUSH DES_KEY_FILE语句告诉 MySQL 从密钥文件中读取新的密钥值。这需要RELOAD特权。

拥有一组默认密钥的一个好处是,它为应用程序提供了一种检查加密列值是否存在的方法,而无需赋予最终用户解密这些值的权利。

Note

从 MySQL 5.7.6 开始不推荐使用DES_ENCRYPT()DES_DECRYPT()函数,这些函数将在以后的 MySQL 版本中删除,并且不再使用。考虑改用AES_ENCRYPT()AES_DECRYPT()

mysql> SELECT customer_address FROM customer_table 
     > WHERE crypted_credit_card = DES_ENCRYPT('credit_card_number');

ENCODE(str,pass_str)

ENCODE()使用* pass_str 作为密码来加密 str 。结果是长度与 str *相同的二进制字符串。要解密结果,请使用DECODE()

Note

ENCODE()DECODE()函数在 MySQL 5.7 中已弃用,将在以后的 MySQL 版本中删除,并且不再使用。

如果仍然需要使用ENCODE(),则必须将其与盐值一起使用以降低风险。例如:

ENCODE('cleartext', CONCAT('my_random_salt','my_secret_password'))

每当更新密码时,都必须使用新的随机盐值。

ENCRYPT(str[,salt])

使用 Unix crypt()系统调用对str *进行加密,并返回二进制字符串。 * salt 参数必须是至少包含两个字符的字符串,否则结果将是NULL。如果未提供 salt *参数,则使用随机值。

Note

从 MySQL 5.7.6 开始不推荐使用ENCRYPT()函数,它将在以后的 MySQL 版本中删除,并且不再使用。对于单向哈希,请考虑改用SHA2()

mysql> SELECT ENCRYPT('hello');
        -> 'VxuFAJXVARROc'

至少在某些系统上,ENCRYPT()忽略* str *的除前八个字符外的所有字符。此行为由基础crypt()系统调用的实现确定。

不建议将ENCRYPT()ucs2utf16utf16leutf32多字节字符集一起使用,因为系统调用期望以零字节结尾的字符串。

如果crypt()在您的系统上不可用(与 Windows 一样),则ENCRYPT()始终返回NULL

MD5(str)

计算字符串的 MD5 128 位校验和。该值以 32 个十六进制数字的字符串形式返回;如果参数为NULL,则返回NULL。例如,该返回值可用作哈希键。请参阅本节开头有关有效存储哈希值的 Comments。

返回值是连接字符集中的字符串。

mysql> SELECT MD5('testing');
        -> 'ae2b1fca515949e5d54fb22b8ed95575'

这是“ RSA Data Security,Inc. MD5 消息摘要算法”。

请参阅本节开头有关 MD5 算法的 Comments。

PASSWORD(str)

Note

从 MySQL 5.7.6 开始不推荐使用此功能,并且在将来的 MySQL 版本中将删除该功能。

返回根据明文密码* str *计算得出的哈希密码字符串。返回值是连接字符集中的字符串;如果参数是NULL,则返回NULL。该函数是服务器用于加密 MySQL 密码以存储在mysql.user授予 table 中的算法的 SQL 接口。

old_passwords系统变量控制PASSWORD()函数使用的密码哈希方法。它还会影响由使用IDENTIFIED BY子句指定密码的CREATE USERGRANT语句执行的密码哈希处理。

下 table 显示了每种密码哈希方法的许可值old_passwords以及哪些身份验证插件使用该哈希方法。

密码哈希方法old_passwords Value关联身份验证插件
MySQL 4.1 本机哈希0mysql_native_password
SHA-256 hashing2sha256_password

SHA-256 密码散列(old_passwords=2)使用随机盐值,这使得来自PASSWORD()的结果不确定。因此,使用此功能的语句对于基于语句的复制并不安全,因此无法存储在查询缓存中。

PASSWORD()执行的加密是单向的(不可逆),但与 Unix 密码使用的加密类型不同。

Note

PASSWORD()由 MySQL Server 中的身份验证系统使用;您不应*在自己的应用程序中使用它。为此,请考虑使用更安全的功能,例如SHA2()。另请参阅RFC 2195,第 2 节(质询-响应身份验证机制(CRAM)),以获取有关在应用程序中安全处理密码和身份验证的更多信息。

Caution

在某些情况下,调用PASSWORD()的语句可能会记录在服务器日志中或 Client 端的历史记录文件中,例如~/.mysql_history,这意味着具有对该信息的读取权限的任何人都可以读取明文密码。有关服务器日志发生这种情况的条件以及如何控制它的信息,请参阅第 6.1.2.3 节“密码和日志记录”。有关 Client 端日志记录的类似信息,请参见第 4.5.1.3 节“ mysqlClient 端记录”

RANDOM_BYTES(len)

此函数返回使用 SSL 库的随机数生成器生成的* len *随机字节的二进制字符串。 * len *的允许值范围是 1 到 1024.超出该范围的值会发生错误。

RANDOM_BYTES()可用于为AES_DECRYPT()AES_ENCRYPT()函数提供初始化向量。为了在这种情况下使用,* len *必须至少为 16.允许使用较大的值,但忽略超过 16 的字节。

RANDOM_BYTES()生成一个随机值,这使其结果不确定。因此,使用此功能的语句对于基于语句的复制是不安全的,并且不能存储在查询缓存中。

SHA1(str), SHA(str)

如 RFC 3174(安全哈希算法)中所述,为字符串计算 SHA-1 160 位校验和。该值以 40 个十六进制数字的字符串形式返回,如果参数为NULL,则返回NULL。此功能的可能用途之一是作为哈希键。请参阅本节开头有关有效存储哈希值的 Comments。 SHA()SHA1()同义。

返回值是连接字符集中的字符串。

mysql> SELECT SHA1('abc');
        -> 'a9993e364706816aba3e25717850c26c9cd0d89d'

可以将SHA1()视为与MD5()相比在密码学上更安全的等效项。但是,请参阅本节开头有关 MD5 和 SHA-1 算法的 Comments。

SHA2(str, hash_length)

计算 SHA-2 系列哈希函数(SHA-224,SHA-256,SHA-384 和 SHA-512)。第一个参数是要散列的纯文本字符串。第二个参数 table 示结果的所需位长,该位的值必须为 224、256、384、512 或 0(等于 256)。如果任一参数为NULL或哈希长度不是允许值之一,则返回值为NULL。否则,函数结果是一个包含所需位数的哈希值。请参阅本节开头有关有效存储哈希值的 Comments。

返回值是连接字符集中的字符串。

mysql> SELECT SHA2('abc', 224);
        -> '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'

仅当 MySQL 已配置有 SSL 支持时,此功能才有效。参见第 6.3 节“使用加密的连接”

从密码学角度来说,SHA2()MD5()SHA1()更安全。

UNCOMPRESS(string_to_uncompress)

解压缩由COMPRESS()函数压缩的字符串。如果参数不是压缩值,则结果为NULL。此函数要求 MySQL 已使用诸如zlib之类的压缩库进行编译。否则,返回值始终为NULL

mysql> SELECT UNCOMPRESS(COMPRESS('any string'));
        -> 'any string'
mysql> SELECT UNCOMPRESS('any string');
        -> NULL

UNCOMPRESSED_LENGTH(compressed_string)

返回压缩字符串在压缩前的长度。

mysql> SELECT UNCOMPRESSED_LENGTH(COMPRESS(REPEAT('a',30)));
        -> 30

VALIDATE_PASSWORD_STRENGTH(str)

给定一个 table 示明文密码的参数,此函数将返回一个整数以指示密码的强度。返回值的范围是 0(弱)到 100(强)。

VALIDATE_PASSWORD_STRENGTH()的密码评估由validate_password插件完成。如果未安装该插件,则该函数始终返回 0.有关安装validate_password的信息,请参见第 6.4.3 节“密码验证插件”。要检查或配置影响密码测试的参数,请检查或设置validate_password实现的系统变量。参见第 6.4.3.2 节“密码验证插件选项和变量”

密码受到越来越严格的测试,返回值反映了满足了哪些测试,如下 table 所示。另外,如果启用了validate_password_check_user_name系统变量并且密码与用户名匹配,则VALIDATE_PASSWORD_STRENGTH()不管设置其他validate_password系统变量如何都返回 0.

Password TestReturn Value
长度<40
长度≥4 且<validate_password_length25
符合 Policy1(LOW)50
符合 Policy2(MEDIUM)75
符合 Policy3(STRONG)100