Oracle和MySQL中的AES加密给出了不同的结果

Oracle和MySQL中的AES加密给出了不同的结果,mysql,oracle,encryption,plsql,Mysql,Oracle,Encryption,Plsql,我需要比较Oracle数据库和MySQL数据库之间的数据 在Oracle中,数据首先使用AES-128算法加密,然后散列。这意味着无法恢复数据并对其进行解密 相同的数据在MySQL和纯文本中都可用。因此,为了比较数据,我尝试了加密MySQL数据,然后对其进行散列,同时遵循在Oracle中完成的相同步骤 -- ORACLE: -- First the key is hashed with md5 to make it a 128bit key: raw_key := DBMS_CRYPTO.Has

我需要比较Oracle数据库和MySQL数据库之间的数据

在Oracle中,数据首先使用
AES-128
算法加密,然后散列。这意味着无法恢复数据并对其进行解密

相同的数据在MySQL和纯文本中都可用。因此,为了比较数据,我尝试了加密MySQL数据,然后对其进行散列,同时遵循在Oracle中完成的相同步骤

-- ORACLE:
-- First the key is hashed with md5 to make it a 128bit key:
raw_key := DBMS_CRYPTO.Hash (UTL_I18N.STRING_TO_RAW ('test_key', 'AL32UTF8'), DBMS_CRYPTO.HASH_MD5);

-- Initialize the encrypted result
encryption_type:= DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5;

-- Then the data is being encrypted with AES:
encrypted_result := DBMS_CRYPTO.ENCRYPT(UTL_I18N.STRING_TO_RAW('test-data', 'AL32UTF8'), encryption_type, raw_key);
经过多次尝试,我终于发现MySQL中的
aes_encrypt
返回的结果与Oracle中的不同

-- ORACLE:
-- First the key is hashed with md5 to make it a 128bit key:
raw_key := DBMS_CRYPTO.Hash (UTL_I18N.STRING_TO_RAW ('test_key', 'AL32UTF8'), DBMS_CRYPTO.HASH_MD5);

-- Initialize the encrypted result
encryption_type:= DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5;

-- Then the data is being encrypted with AES:
encrypted_result := DBMS_CRYPTO.ENCRYPT(UTL_I18N.STRING_TO_RAW('test-data', 'AL32UTF8'), encryption_type, raw_key);
oracle代码的结果将是:
8FCA326C25C8908446D28884394F2E22

-- MySQL
-- While doing the same with MySQL, I have tried the following:
SELECT hex(aes_encrypt('test-data', MD5('test_key'));
MySQL代码的结果将是:
DC7ACAC07F04BBE0ECEC6B6934CF79FE

我错过什么了吗?或者不同语言之间的加密方法不一样

更新: 根据下面的评论,我认为我应该提到这样一个事实:Oracle中的
DBMS\u CRYPTO.Hash
的结果与MySQL中的
MD5
函数返回的结果相同

在Oracle中使用
CBC
CBE
也会得到相同的结果,因为IV没有传递给函数,因此使用了IV的默认值,即
NULL

赏金: 如果有人能验证我的最后一条评论,并且如果在两边使用相同的填充,将产生相同的结果,那么将获得奖励:


@rossum MySQL中的默认填充是PKCS7,mmm。。。哦在甲骨文中 它使用的是PKCS5,真不敢相信我没有注意到。谢谢(顺便说一句 Oracle没有PAD_PKCS7选项,至少在11g中没有)


可能是CBC对ECB。本页底部的评论:mysql函数使用ECB,mysql的MD5函数返回一个由32个十六进制字符组成的字符串。它被标记为二进制字符串,但它不是人们期望的16字节二进制数据

因此,要修复它,必须将此字符串转换回二进制数据:

SELECT hex(aes_encrypt('test-data', unhex(MD5('test_key'))));
结果是:

8FCA326C25C8908446D28884394F2E22
它又是一个由32个十六进制字符组成的字符串。但在其他方面,结果与Oracle相同

顺便说一句:

  • MySQL使用PKCS7填充
  • PKCS5填充和PKCS7填充是相同的。因此,Oracle填充选项是正确的
  • MySQL使用ECB分组密码模式。因此,您必须相应地修改代码。(前16个字节没有任何区别。)
  • MySQL不使用初始化向量(与Oracle代码相同)
  • MySQL使用非标准的折叠键。因此,要在MySQL和Oracle(或.NET或Java)中实现相同的结果,只需使用16字节长的密钥

我只想根据@Codo非常有教育意义的答案给出一个完整的假人解决方案

编辑: 对于一般情况下的精确性,我发现: -“对于8字节块大小,PKCS#5填充是PKCS#7填充的子集”。 因此,严格来说,PKCS5不能应用于AES;他们的意思是PKCS7,但使用他们的 名称可以互换

/*MySQL使用非标准的折叠密钥。 *因此,为了在MySQL和Oracle(或.NET或Java)中实现相同的结果, 仅使用16字节长(32个十六进制符号)=128位的键 AES加密,MySQL AES_加密默认值。 * *这意味着MySQL允许16到32字节之间的任何密钥长度 用于128位AES加密,但标准不允许 AES使用非16字节密钥,因此不要使用它,因为您将无法 要在其他平台中使用标准AES解密密钥,请使用更多 超过16字节,并有义务对 另一个平台中的密钥,以及XOR之类的东西。 (它已经存在了,但为什么要做奇怪的非标准的事情呢 可能会在MySQL决定时更改,等等)。 此外,我想他们会说MySQL为这些问题选择的算法 在安全级别上,案例是一个非常糟糕的选择。。。 */

--####甲骨文:

--首先,使用md5对密钥进行散列,使其成为128位密钥(16字节,32个十六进制符号):

--MySQL至少在默认情况下使用AL32UTF8

--配置加密参数:

encryption_type:= DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_ECB + DBMS_CRYPTO.PAD_PKCS5; 
--严格地说,这真的是PKCS7

/*我选择欧洲央行是因为如果应用和 @科多说这是正确的,但作为标准(甲骨文)AES128将只接受 16字节密钥,CBC也可以工作,因为我相信它们不适用于16字节密钥。 有人能证实这一点吗*/

--然后使用AES对数据进行加密:

encrypted_result := DBMS_CRYPTO.ENCRYPT(UTL_I18N.STRING_TO_RAW('test-data', 'AL32UTF8'), encryption_type, raw_key);
--结果是二进制的(varbinary,blob)

--如果您想用十六进制字符表示,可以使用RAWTOHEX()来表示

如果直接使用十六进制字符表示的16字节散列密码短语或32个十六进制随机字符:

raw_key := HEXTORAW(32_hex_key)
encryption_type := 6 + 768 + 4096 -- (same as above in numbers; see Oracle Docum.) 
raw_data := UTL_I18N.STRING_TO_RAW('test-data', 'AL32UTF8')

encrypted_result := DBMS_CRYPTO.ENCRYPT( raw_data, encryption_type, raw_key )
--ORACLE解密:

decrypted_result := UTL_I18N.RAW_TO_CHAR( CRYPTO.DECRYPT( raw_data, encryption_type, raw_key ), 'AL32UTF8' )
--在SQL中:

--####MySQL解密:

decrypted_result := UTL_I18N.RAW_TO_CHAR( CRYPTO.DECRYPT( raw_data, encryption_type, raw_key ), 'AL32UTF8' )
--MySQL的MD5函数返回一个由32个十六进制字符组成的字符串(=16字节=128位)

--它被标记为二进制字符串,但它不是人们期望的16字节二进制数据

--注意:请注意,MD5、SHA1等函数的返回类型在5.3.x之后的一些版本中发生了变化。请参阅MySQL 5.7手册

--因此,要修复它,必须使用unHex()将该字符串从十六进制转换回二进制数据:

附言: 我建议阅读MySQL 5.7手册中改进的解释,而且现在允许更多的配置。

乍一看,我怀疑问题出在这里:
'test_key',AL32UTF8'
我认为MySQL在加密之前对您的数据有不同的字符集,因此不同的数据。我怀疑加密应该是相同的,所以我会确保密钥是相同的。i、 MD5是否返回十六进制字符串或原始字节。如果是十六进制,那么情况如何?不幸的是,在安装了DBMS_CRYPTO的情况下,我无法访问Oracle。@是的,MD5哈希在两个数据库中给出了相同的结果,即
8C32D1183251DF982
SELECT hex(aes_encrypt('test-data', unhex(MD5('test_key')));