2011-09-01 271 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

相關問題