Lockbox3 AES128加密与Oracle PLSQL解密结果不一致问题求助
Hey there, let's work through this AES 128 encryption mismatch between Delphi (RAD Studio Tokyo 10.2 with Lockbox3) and Oracle. This is a super common issue with cross-library encryption, and 9 times out of 10 it boils down to mismatched settings rather than a bug in either tool. Let's break down the most likely culprits and how to test them.
Top Causes to Check First
1. Padding Mode Inconsistencies
This is the #1 suspect for your issue. Even small differences in padding will completely break encryption consistency.
- Lockbox3 Default: Lockbox3 uses
pmPKCS7as its default padding mode. You can explicitly set it in code to avoid surprises:FCodec.PaddingMode := pmPKCS7; - Oracle Default: If you're using
DBMS_CRYPTO, Oracle defaults toPKCS5_PADDINGfor AES. While PKCS5 is technically a subset of PKCS7 (and identical for 128-bit blocks), it's safer to explicitly specify the padding mode in your Oracle call to match Lockbox3:DBMS_CRYPTO.AES_CBC_PKCS7_PADDING -- Use this if your Oracle version supports it -- Or PKCS5 if PKCS7 isn't available (they work the same for AES128) DBMS_CRYPTO.AES_CBC_PKCS5_PADDING - If either side uses zero-padding or no padding, your results will never match—double-check this first.
2. Mismatched Initialization Vector (IV)
AES in CBC mode (the default for both Lockbox3 and Oracle) requires an identical IV on both encryption and decryption sides.
- Lockbox3 Gotcha: If you don't explicitly set the IV, Lockbox3 generates a random one each time you encrypt. This means every encryption of the same plaintext will produce a different result. For testing, set a fixed IV:
// 16-byte IV (matches AES128 block size) FCodec.IV := TBytes.Create($00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0A, $0B, $0C, $0D, $0E, $0F); - Oracle Side: Use the exact same IV value in your encryption call. Convert it to a RAW type like this:
v_iv RAW(16) := UTL_RAW.CAST_FROM_HEX('000102030405060708090A0B0C0D0E0F');
3. Key Handling Differences
How you convert your keyText string into a 16-byte AES128 key must be identical on both sides.
- Delphi Check: Are you using the raw UTF-8 bytes of
keyTextdirectly as the key, or hashing it first? For example, if you're using SHA256 to hash the key string then taking the first 16 bytes, your code might look like this:var KeyHash: TIdHashSHA256; KeyBytes: TBytes; begin KeyHash := TIdHashSHA256.Create; try KeyBytes := KeyHash.HashStringAsBytes(KeyText, IndyTextEncoding_UTF8); // Take first 16 bytes for AES128 SetLength(KeyBytes, 16); FCodec.SetKey(KeyBytes, Length(KeyBytes)*8); finally KeyHash.Free; end; end; - Oracle Match: You need to replicate this exact logic in Oracle. For the SHA256 example:
v_key_raw RAW(32) := DBMS_CRYPTO.HASH(UTL_I18N.STRING_TO_RAW(v_key, 'AL32UTF8'), DBMS_CRYPTO.SHA256); v_aes_key RAW(16) := SUBSTR(v_key_raw, 1, 16); -- Take first 16 bytes - Never assume "the key is the same string"—always verify the raw byte values match between Delphi and Oracle.
4. Encoding & Block/Key Size
- Plaintext Encoding: You're using
utf8stringin Delphi, so make sure Oracle is also converting the plaintext to UTF-8 bytes before encryption:UTL_I18N.STRING_TO_RAW(v_plaintext, 'AL32UTF8') - AES128 Confirmation: Double-check both sides are using AES128 specifically (16-byte key, 16-byte block size). In Lockbox3:
FCodec.AlgorithmID := ALG_RIJNDAEL; -- AES is a subset of Rijndael FCodec.KeySize := ks128; FCodec.ChainMode := cmCBC; -- Match Oracle's mode
Test with Minimal, Reproducible Code
To eliminate variables, use these simplified snippets to test encryption on both sides and compare the base64-encoded results.
Delphi Test Code
uses uTPLb_Constants, uTPLb_BaseNonVisualComponent, uTPLb_Codec, uTPLb_CryptographicLibrary; procedure TestAESConsistency; var FCryptoLib: TCryptographicLibrary; FCodec: TCodec; PlainText: UTF8String; KeyText: UTF8String; CipherBase64: string; FixedIV: TBytes; begin FCryptoLib := TCryptographicLibrary.Create(nil); FCodec := TCodec.Create(nil); try FCodec.CryptoLibrary := FCryptoLib; // Configure AES128 CBC with PKCS7 padding FCodec.AlgorithmID := ALG_RIJNDAEL; FCodec.KeySize := ks128; FCodec.ChainMode := cmCBC; FCodec.PaddingMode := pmPKCS7; // Fixed test key (16-byte UTF8 string) KeyText := 'MyTestKey12345678'; FCodec.Password := KeyText; // Fixed IV FixedIV := TBytes.Create($00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$0B,$0C,$0D,$0E,$0F); FCodec.IV := FixedIV; // Test plaintext PlainText := 'Hello AES Test!'; // Encrypt to Base64 FCodec.EncryptString(PlainText, CipherBase64); ShowMessage('Delphi Cipher (Base64): ' + CipherBase64); finally FCodec.Free; FCryptoLib.Free; end; end;
Oracle Test Code
DECLARE v_plaintext VARCHAR2(100) := 'Hello AES Test!'; v_key VARCHAR2(100) := 'MyTestKey12345678'; -- Match Delphi's KeyText v_iv RAW(16) := UTL_RAW.CAST_FROM_HEX('000102030405060708090A0B0C0D0E0F'); -- Match Delphi's IV v_cipher_raw RAW(2000); v_cipher_base64 VARCHAR2(2000); BEGIN v_cipher_raw := DBMS_CRYPTO.ENCRYPT( src => UTL_I18N.STRING_TO_RAW(v_plaintext, 'AL32UTF8'), typ => DBMS_CRYPTO.AES_CBC_PKCS5_PADDING, -- Equivalent to PKCS7 for AES128 key => UTL_I18N.STRING_TO_RAW(v_key, 'AL32UTF8'), iv => v_iv ); v_cipher_base64 := UTL_ENCODE.BASE64_ENCODE(v_cipher_raw); DBMS_OUTPUT.PUT_LINE('Oracle Cipher (Base64): ' || v_cipher_base64); END; /
Final Troubleshooting Step
If the test code still doesn't produce matching results, print out the raw byte values of the plaintext, key, and IV from both sides. A single mismatched byte anywhere in these will cause the encryption results to differ entirely.
内容的提问来源于stack exchange,提问作者Faadiel

