2011-09-01 280 views
7

我需要比较Oracle数据库和MySQL数据库之间的数据。Oracle和MySQL中的AES加密给出了不同的结果

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

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

经过大量尝试后,我终于发现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

我缺少的东西?或者不同语言之间的加密方法不一样?

UPDATE: 根据下面的评论,我相信我应该提的是,在甲骨文DBMS_CRYPTO.Hash的结果是一样的,通过在MySQL中MD5函数返回的结果。

另外使用CBC或者Oracle CBE给出相同的结果,由于IV不被传递给函数,从而使用IV的缺省值,它是NULL

BOUNTY: 如果有人可以验证我最后的评论,而事实上,如果使用两面相同的填充,会产生相同的结果得到的赏金:

@rossum在MySQL默认填充是PKCS7,嗯...呵呵..在Oracle 它使用PK CS5,不敢相信我没注意到。谢谢。 (Btw Oracle没有PAD_PKCS7选项,至少不在11g中)

+0

乍一看我怀疑问题在这里:''test_key','AL32UTF8''方法MySQL在你的数据上有不同的字符集,因此在应用加密之前有不同的数据。 – Johan

+0

我怀疑加密应该是相同的,所以我会确保密钥是一样的。即MD5是否返回十六进制字符串或原始字节。如果是十六进制,那么情况如何?不幸的是我没有安装DBMS_CRYPTO的权限。 – Sodved

+0

@Sodved是的MD5哈希在两个数据库'8C32D1183251DF9828F929B935AE0419'中给出了相同的结果。由于@Johan就是这种情况,它不应该是一个编码问题,因为MD5哈希是相同的 – Dan

回答

8

MySQL的MD5函数返回一个32个十六进制字符的字符串。它被标记为二进制字符串,但它不是人们所期望的16字节二进制数据。

因此,要解决这个问题,这个字符串必须被转换回二进制数据:

SELECT hex(aes_encrypt('test-data', unhex(MD5('test_key')))); 

结果是:

8FCA326C25C8908446D28884394F2E22 

它再次的32个十六进制字符的字符串。但除此之外,这与Oracle的结果是一样的。

而且BTW:

  • MySQL使用PKCS7填充。
  • PKCS5填充和PKCS7填充是一样的。所以Oracle填充选项是正确的。
  • MySQL使用ECB分组密码模式。所以你必须相应地调整代码。 (它对前16个字节没有任何影响。)
  • MySQL不使用初始化向量(与您的Oracle代码相同)。
  • MySQL使用非标准折叠键。因此,要在MySQL和Oracle(或.NET或Java)中实现相同的结果,只能使用长度为16个字节的密钥。
0

可能是CBC vs ECB。评论在此页的底部:http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html说mysql函数使用ECB

+0

我已经看到了那条评论,并且我已经在Oracle中尝试了ECB,看它是否给出了不同的结果,但事实并非如此。实际上,这是预期的结果,因为如果IV未被发送到加密功能,ECB和CBC基本相同。 (它的默认值是'NULL') – Dan

+0

@Dan:如果一个块被加密,它们只是相同的。使用填充时,由于填充,刚开始的单个块(16个字节)可能会扩展到两个块。 – rossum

1

只想根据@ Codo的非常教学的答案给出完整的假人解决方案。

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

About PKCS5 and PKCS7

/* MySQL使用非标准折叠的关键。 *所以要在MySQL和Oracle(或.NET或Java)中实现相同的结果, 只使用长度为16个字节(32个十六进制符号)= 128位的密钥 AES加密,MySQL AES_encrypt默认为1。 * *这意味着MySQL允许使用16位和32位字节的密钥长度为 (对于128位AES加密),但标准 AES不允许使用非16位字节的密钥,所以请不要使用它, t能够 在其他平台上使用标准AES解密密钥的方式,其密钥的格式为 ,而不是16个字节,并且需要编写其他平台的密钥的 的MySQL折叠,以及XOR的东西等。 已经在那里,但为什么做奇怪的非标准的事情,当MySQL决定时可能会改变,等等)。 此外,我觉得他们说的由MySQL对于那些 情况下选择的算法是一个安全级别一个非常糟糕的选择... */

- ### ORACLE:

- 首先,密钥被散列用MD5,使其128位密钥(16个字节,32进制符号):

raw_key := DBMS_CRYPTO.Hash (UTL_I18N.STRING_TO_RAW ('test_key', 'AL32UTF8'), DBMS_CRYPTO.HASH_MD5); 

- MySQL使用AL32UTF8,至少默认

- 配置加密参数:

encryption_type:= DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_ECB + DBMS_CRYPTO.PAD_PKCS5; 

- 严格地说,它确实是PKCS7。

/*和我选择了速度更快ECB如果应用和 @Codo说这是正确的,但作为标准(Oracle)的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:

SELECT 
    UTL_I18N.RAW_TO_CHAR( 
    DBMS_CRYPTO.DECRYPT( 
    UTL_I18N.STRING_TO_RAW('test-data', 'AL32UTF8'), 
    6 + 768 + 4096, 
    HEXTORAW(32_hex_key) 
) , 'AL32UTF8') as "decrypted" 
FROM DUAL; 

- ### MySQL解密:

- MySQL的MD5函数返回一个32个十六进制字符(= 16字节= 128位)的字符串。

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

- 注意:注意MD5,SHA1等函数的返回类型在5.3.x以后的某些版本中发生了变化。查看MySQL 5.7手册。

- 因此,要解决这个问题,这个字符串必须从十六进制转换为二进制数据与UNHEX():

SELECT hex(aes_encrypt('test-data', unhex(MD5('test_key'))); 

PS: 我建议阅读在MySQL 5.7手动改进解释,而且现在允许更多的配置。 MySQL AES_ENCRYPT improved explanation from v5.7 manual

相关问题