.NET到Oracle 11g的3DES加密迁移:加密结果不一致问题
.NET 3DES加密迁移至Oracle 11g结果不一致问题
.NET端实现代码及输出
using System; using System.Security.Cryptography; using System.Text; using System.Linq; public class Program { private const string strKey = "H9p-S5m2Dh6Fe7Ls3-Qs1D2e4Z"; public static void Main() { var strToEncrypt = "test"; TripleDESCryptoServiceProvider objDESCrypto = new TripleDESCryptoServiceProvider(); MD5CryptoServiceProvider objHashMD5 = new MD5CryptoServiceProvider(); byte[] byteHash, byteBuff; byteHash = objHashMD5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(strKey)); Console.WriteLine("v_key net = "+BitConverter.ToString(byteHash).Replace("-","")); objHashMD5 = null; objDESCrypto.Key = byteHash; objDESCrypto.Mode = CipherMode.CBC; //CBC, CFB objDESCrypto.Padding = PaddingMode.Zeros; Console.WriteLine("iv net = "+BitConverter.ToString(stringToHex("0123456789ABCDEF")).Replace("-","")); objDESCrypto.IV = stringToHex("0123456789ABCDEF"); byteBuff = ASCIIEncoding.ASCII.GetBytes(strToEncrypt); Console.WriteLine("src net = "+BitConverter.ToString(byteBuff).Replace("-","")); var hashbyte = objDESCrypto.CreateEncryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length); Console.WriteLine("encrypted_str net = "+BitConverter.ToString(hashbyte).Replace("-","")); Console.WriteLine("encrypted_str64 net = " + Convert.ToBase64String(objDESCrypto.CreateEncryptor(). TransformFinalBlock(hashbyte, 0, hashbyte.Length))); var decrypted_str = ASCIIEncoding.ASCII.GetString (objDESCrypto.CreateDecryptor().TransformFinalBlock (hashbyte, 0, hashbyte.Length)); Console.WriteLine("decrypted_str net = "+decrypted_str); string hexString = "43480170"; } public static byte[] stringToHex(string hexString) { return Enumerable.Range(0, hexString.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hexString.Substring(x, 2), 16)) .ToArray(); } }
输出:
v_key net = 8B928C8346648BDDB14B627EFF4E66F1 iv net = 0123456789ABCDEF src net = 74657374 encrypted_str net = F7BC988C0EA6EEE5 encrypted_str64 net = 73A6DqCayTc= decrypted_str net = test
Oracle 11g存储过程及输出
create or replace PACKAGE BODY Criptografia AS -- 加密密钥(确保与C#端一致) strKeyDataAD CONSTANT VARCHAR2(26) := 'H9p-S5m2Dh6Fe7Ls3-Qs1D2e4Z'; PROCEDURE EncryptDataAD(strToEncrypt IN VARCHAR2) AS l_mod_ecb pls_integer; v_key raw(32); encrypted_str VARCHAR2(4000); encryptedBase64_str VARCHAR2(4000); decrypted_str VARCHAR2(4000); BEGIN l_mod_ecb := dbms_crypto.ENCRYPT_3DES+ dbms_crypto.CHAIN_CBC + DBMS_CRYPTO.PAD_ZERO ; dbms_output.put_line('l_mod_ecb oracle: '|| l_mod_ecb); v_key := dbms_crypto.hash( utl_i18n.string_to_raw(strKeyDataAD, 'AL32UTF8'), dbms_crypto.hash_md5 ); dbms_output.put_line('v_key oracle: '|| (v_key)); dbms_output.put_line('src oracle: '|| utl_i18n.string_to_raw(strToEncrypt, 'AL32UTF8')); encrypted_str := dbms_crypto.encrypt( src => utl_i18n.string_to_raw(strToEncrypt, 'AL32UTF8'), typ => dbms_crypto.ENCRYPT_3DES + dbms_crypto.CHAIN_CBC + DBMS_CRYPTO.PAD_ZERO, key => dbms_crypto.hash( utl_i18n.string_to_raw(strKeyDataAD, 'AL32UTF8'), dbms_crypto.hash_md5 ) ,iv => hextoraw('0123456789ABCDEF') ); dbms_output.put_line('encrypted_str oracle: '|| encrypted_str); dbms_output.put_line('encryptedBase64_str oracle: '|| utl_raw.cast_to_varchar2(utl_encode.base64_encode(encrypted_str))); decrypted_str := dbms_crypto.decrypt( src => encrypted_str, typ => dbms_crypto.ENCRYPT_3DES + dbms_crypto.CHAIN_CBC + DBMS_CRYPTO.PAD_ZERO, key => dbms_crypto.hash( utl_i18n.string_to_raw(strKeyDataAD, 'AL32UTF8'), dbms_crypto.hash_md5 ) ,iv => hextoraw ( '0123456789ABCDEF' ) ); dbms_output.put_line('decrypted_str oracle: '|| UTL_I18N.RAW_TO_CHAR(decrypted_str,'AL32UTF8')); END EncryptDataAD; END Criptografia;
输出:
l_mod_ecb oracle: 12545 v_key oracle: 8B928C8346648BDDB14B627EFF4E66F1 src oracle: 74657374 encrypted_str oracle: 28F7DA0F3B338BB1 encryptedBase64_str oracle: KPfaDzszi7E= decrypted_str oracle: test
问题描述
已确认MD5哈希后的密钥(v_key)和输入字符串的字节(src)完全一致,但加密结果仍不相同。尝试调整Oracle端的填充方式(PAD_PKCS5、PAD_NONE、PAD_ZERO)和链模式(CHAIN_ECB、CHAIN_CBC、CHAIN_CFB、CHAIN_OFB),均无法匹配.NET端的加密结果。
解决方案
问题核心在于.NET和Oracle对3DES密钥的处理逻辑不同:
- .NET的
TripleDESCryptoServiceProvider在传入16字节密钥时,会自动将其扩展为24字节(将前8字节复制并追加到原密钥末尾),使用DES-EDE3模式。 - Oracle的
dbms_crypto.ENCRYPT_3DES在传入16字节密钥时,直接使用该16字节作为DES-EDE模式的密钥,不会自动扩展。
修改Oracle存储过程,手动将16字节的MD5哈希密钥扩展为24字节,即可得到与.NET一致的加密结果:
create or replace PACKAGE BODY Criptografia AS strKeyDataAD CONSTANT VARCHAR2(26) := 'H9p-S5m2Dh6Fe7Ls3-Qs1D2e4Z'; PROCEDURE EncryptDataAD(strToEncrypt IN VARCHAR2) AS l_mod_ecb pls_integer; v_key_raw16 raw(16); v_key_raw24 raw(24); -- 扩展后的24字节密钥 encrypted_str VARCHAR2(4000); encryptedBase64_str VARCHAR2(4000); decrypted_str VARCHAR2(4000); BEGIN l_mod_ecb := dbms_crypto.ENCRYPT_3DES + dbms_crypto.CHAIN_CBC + DBMS_CRYPTO.PAD_ZERO; dbms_output.put_line('l_mod_ecb oracle: '|| l_mod_ecb); -- 获取16字节MD5哈希密钥,用US7ASCII匹配.NET的ASCII编码 v_key_raw16 := dbms_crypto.hash( utl_i18n.string_to_raw(strKeyDataAD, 'US7ASCII'), dbms_crypto.hash_md5 ); -- 扩展为24字节:将前8字节追加到16字节密钥末尾 v_key_raw24 := v_key_raw16 || SUBSTR(v_key_raw16, 1, 8); dbms_output.put_line('v_key oracle (24字节): '|| v_key_raw24); dbms_output.put_line('src oracle: '|| utl_i18n.string_to_raw(strToEncrypt, 'US7ASCII')); encrypted_str := dbms_crypto.encrypt( src => utl_i18n.string_to_raw(strToEncrypt, 'US7ASCII'), typ => l_mod_ecb, key => v_key_raw24, iv => hextoraw('0123456789ABCDEF') ); dbms_output.put_line('encrypted_str oracle: '|| encrypted_str); dbms_output.put_line('encryptedBase64_str oracle: '|| utl_raw.cast_to_varchar2(utl_encode.base64_encode(encrypted_str))); decrypted_str := dbms_crypto.decrypt( src => encrypted_str, typ => l_mod_ecb, key => v_key_raw24, iv => hextoraw('0123456789ABCDEF') ); dbms_output.put_line('decrypted_str oracle: '|| UTL_I18N.RAW_TO_CHAR(decrypted_str,'US7ASCII')); END EncryptDataAD; END Criptografia;
修改说明
- 密钥扩展:将16字节的MD5哈希密钥,通过
SUBSTR(v_key_raw16, 1, 8)截取前8字节并追加到原密钥后,得到24字节密钥,与.NET的处理逻辑对齐。 - 编码对齐:将Oracle中的字符编码从
AL32UTF8改为US7ASCII,确保与.NET的ASCIIEncoding.ASCII完全匹配,避免潜在编码差异问题。
修改后运行,Oracle端的加密结果将与.NET端一致:F7BC988C0EA6EEE5,Base64结果为73A6DqCayTc=。
内容的提问来源于stack exchange,提问作者Leandro Locani
相关产品推荐
相关产品推荐

