简体   繁体   English

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

[英]AES Encryption in Oracle and MySQL are giving different results

I am in need to compare data between an Oracle database and a MySQL database. 我需要比较Oracle数据库和MySQL数据库之间的数据。

In Oracle, the data is first encrypted with the AES-128 algorithm, and then hashed. 在Oracle中,首先使用AES-128算法对数据进行加密,然后进行散列。 Which means it is not possible to recover the data and decrypt it. 这意味着无法恢复数据并对其进行解密。

The same data is available in MySQL, and in plain text. MySQL和纯文本中提供了相同的数据。 So to compare the data, I tried encrypting and then hashing the MySQL data while following the same steps done in Oracle. 因此,为了比较数据,我尝试加密然后散列MySQL数据,同时遵循Oracle中完成的相同步骤。

After lots of tries, I finally found out that the aes_encrypt in MySQL returns different results than the one in 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);

The result for the oracle code will be: 8FCA326C25C8908446D28884394F2E22 oracle代码的结果将是: 8FCA326C25C8908446D28884394F2E22

-- MySQL
-- While doing the same with MySQL, I have tried the following:
SELECT hex(aes_encrypt('test-data', MD5('test_key'));

The result for the MySQL code will be: DC7ACAC07F04BBE0ECEC6B6934CF79FE MySQL代码的结果将是: DC7ACAC07F04BBE0ECEC6B6934CF79FE

Am I missing something? 我错过了什么吗? Or are the encryption methods between different languages not the same? 或者不同语言之间的加密方法不一样?

UPDATE: According to the comments below, I believe I should mention the fact that the result of DBMS_CRYPTO.Hash in Oracle is the same as the result returned by the MD5 function in MySQL. 更新:根据下面的评论,我相信我应该提到Oracle中DBMS_CRYPTO.Hash的结果与MySQL中MD5函数返回的结果相同的事实。

Also using CBC or CBE in Oracle gives the same result, since the IV isn't being passed to the function, thus the default value of the IV is used which is NULL 在Oracle中使用CBCCBE也会得到相同的结果,因为IV没有传递给函数,因此使用的是IV的默认值,即NULL

BOUNTY: If someone can verify my last comment, and the fact that if using same padding on both sides, will yield same results gets the bounty: BOUNTY:如果有人可以验证我的最后评论,并且如果双方使用相同的填充,将产生相同的结果获得赏金:

@rossum The default padding in MySQL is PKCS7, mmm... Oh.. In Oracle it's using PKCS5, can't believe I didn't notice that. @rossum MySQL中的默认填充是PKCS7,mmm ......哦..在Oracle中它使用PKCS5,简直不敢相信我没注意到。 Thanks. 谢谢。 (Btw Oracle doesn't have the PAD_PKCS7 option, not in 11g at least) (顺便说一下Oracle没有PAD_PKCS7选项,至少不是11g)

MySQL's MD5 function returns a string of 32 hexadecimal characters. MySQL的MD5函数返回一个32个十六进制字符的字符串。 It's marked as a binary string but it isn't the 16 byte binary data one would expect. 它被标记为二进制字符串,但它不是人们期望的16字节二进制数据。

So to fix it, this string must be converted back to the binary data: 所以要修复它,必须将此字符串转换回二进制数据:

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

The result is: 结果是:

8FCA326C25C8908446D28884394F2E22

It's again a string of 32 hexadecimal characters. 它又是一个由32个十六进制字符组成的字符串。 But otherwise it's the same result as with Oracle. 但是否则它与Oracle的结果相同。

And BTW: 而BTW:

  • MySQL uses PKCS7 padding. MySQL使用PKCS7填充。
  • PKCS5 padding and PKCS7 padding are one and the same. PKCS5填充和PKCS7填充是同一个。 So the Oracle padding option is correct. 所以Oracle填充选项是正确的。
  • MySQL uses ECB block cipher mode. MySQL使用ECB分组密码模式。 So you'll have to adapt the code accordingly. 因此,您必须相应地调整代码。 (It doesn't make any difference for the first 16 bytes.) (它对前16个字节没有任何影响。)
  • MySQL uses no initialization vector (the same as your Oracle code). MySQL不使用初始化向量(与Oracle代码相同)。
  • MySQL uses a non-standard folding a keys. MySQL使用非标准折叠键。 So to achieve the same result in MySQL and Oracle (or .NET or Java), only use keys that are 16 byte long. 因此,要在MySQL和Oracle(或.NET或Java)中实现相同的结果,只使用16字节长的密钥。

Just would like to give the complete solution for dummies based on @Codo's very didactic answer. 我想根据@ Codo非常有说服力的答案给出完整的假人解决方案。

EDIT: For being exact in general cases, I found this: - "PKCS#5 padding is a subset of PKCS#7 padding for 8 byte block sizes". 编辑:在一般情况下,我发现这一点: - “PKCS#5填充是PKCS#7填充的子集,用于8字节块大小”。 So strictly PKCS5 can't be applied to AES; 因此严格的PKCS5不能应用于AES; they mean PKCS7 but use their names interchangeably. 他们的意思是PKCS7,但可以互换使用他们的名字。

About PKCS5 and PKCS7 关于PKCS5和PKCS7

/* MySQL uses a non-standard folding a key. / * MySQL使用非标准折叠键。 * So to achieve the same result in MySQL and Oracle (or .NET or Java), only use keys that are 16 bytes long (32 hexadecimal symbols) = 128 bits AES encryption, the MySQL AES_encrypt default one. *因此,要在MySQL和Oracle(或.NET或Java)中实现相同的结果,只使用16字节长的密钥(32个十六进制符号)= 128位AES加密,MySQL AES_encrypt默认密钥。 * * This means MySQL admits any key length between 16 and 32 bytes for 128 bits AES encryption, but it's not allowed by the standard AES to use a non-16 bytes key, so do not use it as you won't be able to use the standard AES decrypt in other platform for keys with more than 16 bytes, and would be obligued to program the MySQL folding of the key in that other platform, with the XOR stuff, etc. (it's already out there but why doing weird non-standard things thay may change when MySQL decide, etc.). * *这意味着对于128位AES加密,MySQL允许16到32字节之间的任何密钥长度,但标准AES不允许使用非16字节密钥,因此不要使用它,因为您将无法使用在其他平台上使用标准的AES解密来获得超过16个字节的密钥,并且有义务在其他平台上编写密钥的MySQL折叠,使用XOR等等(它已经在那里,但为什么要做奇怪的非当MySQL决定等时,标准的东西可能会改变。 Moreover, I think they say the algorithm chosen by MySQL for those cases is a really bad choose on a security level... */ 而且,我认为他们说MySQL为这些案例选择的算法在安全级别上是一个非常糟糕的选择... * /

-- ### ORACLE: - ### ORACLE:

-- First the key is hashed with md5 to make it a 128 bit key (16 bytes, 32 hex symbols): - 首先使用md5对密钥进行哈希处理,使其成为128位密钥(16字节,32个十六进制符号):

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

-- MySQL uses AL32UTF8, at least by default - MySQL至少在默认情况下使用AL32UTF8

-- Configure the encryption parameters: - 配置加密参数:

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

-- Strictly speaking, it's really PKCS7. - 严格来说,它确实是PKCS7。

/* And I choose ECB for being faster if applied and @Codo said it's the correct one, but as standard (Oracle) AES128 will only accept 16 bytes keys, CBC also works, as I believe they are not applied to a 16 bytes key. / *我选择ECB是为了更快,如果应用和@Codo说它是正确的,但作为标准(Oracle)AES128只接受16字节密钥,CBC也可以工作,因为我认为它们不适用于16字节密钥。 Could someone confirm this? 有人能证实吗? */ * /

-- Then the data is encrypted with AES: - 然后用AES加密数据:

encrypted_result := DBMS_CRYPTO.ENCRYPT(UTL_I18N.STRING_TO_RAW('test-data', 'AL32UTF8'), encryption_type, raw_key);

-- The result is binary (varbinary, blob). - 结果是二进制(varbinary,blob)。

-- One can use RAWTOHEX() for if you want to represent it in hex characters. - 如果要以十六进制字符表示,可以使用RAWTOHEX()。

In case you use directly the 16 bytes hashed passphrase in hex characters representation or 32 hex random chars: 如果直接使用十六进制字符表示的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 Decryption: - ORACLE解密:

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

-- In SQL: - 在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 decryption: - ### MySQL解密:

-- MySQL's MD5 function returns a string of 32 hexadecimal characters (=16 bytes=128 bits). - MySQL的MD5函数返回一个32个十六进制字符的字符串(= 16字节= 128位)。

-- It's marked as a binary string but it isn't the 16 bytes binary data one would expect. - 它被标记为二进制字符串,但它不是人们期望的16字节二进制数据。

-- NOTE: Note that the kind of return of MD5, SHA1, etc functions changed in some versions since 5.3.x. - 注意:请注意,自5.3.x以来,某些版本中MD5,SHA1等功能的返回类型已更改。 See MySQL 5.7 manual. 请参阅MySQL 5.7手册。

-- So to fix it, this string must be converted back from hex to binary data with unHex(): - 所以要修复它,必须使用unHex()将此字符串从十六进制转换回二进制数据:

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

PS: I would recommend to read the improved explanation in MySQL 5.7 Manual, which moreover now allows a lot more configuration. PS:我建议阅读MySQL 5.7手册中的改进说明,此外现在允许更多配置。 MySQL AES_ENCRYPT improved explanation from v5.7 manual MySQL AES_ENCRYPT改进了v5.7手册中的解释

Could be CBC vs ECB. 可能是CBC对欧洲央行。 Comment at the bottom of this page: http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html says mysql function uses ECB 评论在本页底部: http//dev.mysql.com/doc/refman/5.5/en/encryption-functions.html说mysql函数使用ECB

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM