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

发布于 2024-12-02 11:44:38 字数 1525 浏览 2 评论 0原文

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

在 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

我错过了什么吗?还是不同语言之间的加密方式不一样?

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

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

赏金: 如果有人可以验证我的最后评论,并且如果两侧使用相同的填充,将产生相同的结果这一事实将获得赏金:

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

I am in need to compare data between an Oracle database and a MySQL database.

In Oracle, the data is first encrypted with the AES-128 algorithm, and then hashed. Which means it is not possible to recover the data and decrypt it.

The same data is available in MySQL, and in plain text. So to compare the data, I tried encrypting and then hashing the MySQL data while following the same steps done in Oracle.

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

-- 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

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.

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

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:

@rossum The default padding in MySQL is PKCS7, mmm... Oh.. In Oracle
it's using PKCS5, can't believe I didn't notice that. Thanks. (Btw
Oracle doesn't have the PAD_PKCS7 option, not in 11g at least)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夜巴黎 2024-12-09 11:44:38

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

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

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

结果是:

8FCA326C25C8908446D28884394F2E22

它又是一个 32 个十六进制字符的字符串。但除此之外,结果与 Oracle 相同。

顺便说一句:

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

MySQL's MD5 function returns a string of 32 hexadecimal characters. It's marked as a binary string but it isn't the 16 byte binary data one would expect.

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. But otherwise it's the same result as with Oracle.

And BTW:

  • MySQL uses PKCS7 padding.
  • PKCS5 padding and PKCS7 padding are one and the same. So the Oracle padding option is correct.
  • MySQL uses ECB block cipher mode. So you'll have to adapt the code accordingly. (It doesn't make any difference for the first 16 bytes.)
  • MySQL uses no initialization vector (the same as your Oracle code).
  • MySQL uses a non-standard folding a keys. So to achieve the same result in MySQL and Oracle (or .NET or Java), only use keys that are 16 byte long.
悲欢浪云 2024-12-09 11:44:38

只是想根据 @Codo 非常有说教性的答案为傻瓜提供完整的解决方案。

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

关于 PKCS5 和 PKCS7

/* MySQL 使用非标准的折叠键。
* 因此,要在 MySQL 和 Oracle(或 .NET 或 Java)中获得相同的结果,
仅使用 16 字节长(32 个十六进制符号)= 128 位的密钥
AES加密,MySQL AES_crypt默认之一。
*
* 这意味着 MySQL 接受 16 到 32 字节之间的任何密钥长度
用于 128 位 AES 加密,但标准不允许
AES 使用非 16 字节密钥,因此不要使用它,因为您将无法使用它
在其他平台上使用标准 AES 解密来获取更多密钥
超过 16 个字节,并且必须对 MySQL 折叠进行编程
其他平台中的密钥,以及 XOR 等。
(它已经在那里了,但为什么要做奇怪的非标准事情
当 MySQL 决定等时可能会改变)。
此外,我认为他们说MySQL为那些选择的算法
从安全角度来看,cases 是一个非常糟糕的选择......
*/

-- ### 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。

/* 我选择欧洲央行是因为如果应用的话会更快
@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 字节二进制数据。

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

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

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

PS:
我建议阅读 MySQL 5.7 手册中改进的解释,而且现在允许更多配置。
MySQL AES_ENCRYPT 改进了 v5.7 手册中的解释< /a>

Just would like to give the complete solution for dummies based on @Codo's very didactic answer.

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".
So strictly PKCS5 can't be applied to AES; they mean PKCS7 but use their
names interchangeably.

About PKCS5 and PKCS7

/* MySQL uses a non-standard folding a key.
* 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.
*
* 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.).
Moreover, I think they say the algorithm chosen by MySQL for those
cases is a really bad choose on a security level...
*/

-- ### ORACLE:

-- First the key is hashed with md5 to make it a 128 bit key (16 bytes, 32 hex symbols):

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

-- MySQL uses AL32UTF8, at least by default

-- Configure the encryption parameters:

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

-- Strictly speaking, it's really 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.
Could someone confirm this? */

-- Then the data is encrypted with AES:

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

-- The result is binary (varbinary, blob).

-- One can use RAWTOHEX() for if you want to represent it in hex characters.

In case you use directly the 16 bytes hashed passphrase in hex characters representation or 32 hex random chars:

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:

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

-- In 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's MD5 function returns a string of 32 hexadecimal characters (=16 bytes=128 bits).

-- It's marked as a binary string but it isn't the 16 bytes binary data one would expect.

-- NOTE: Note that the kind of return of MD5, SHA1, etc functions changed in some versions since 5.3.x. See MySQL 5.7 manual.

-- So to fix it, this string must be converted back from hex to binary data with unHex():

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

P.S.:
I would recommend to read the improved explanation in MySQL 5.7 Manual, which moreover now allows a lot more configuration.
MySQL AES_ENCRYPT improved explanation from v5.7 manual

橘味果▽酱 2024-12-09 11:44:38

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

Could be CBC vs ECB. Comment at the bottom of this page: http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html says mysql function uses ECB

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文