steve faleiro 1 Posted January 20, 2021 (edited) I've posted my main question on StackOverflow here, and am repeating below: I'm encrypting text in PHP (openssl_encrypt / 'aes-256-cbc') and then trying to decrypt it in Delphi 7 (DCPCrypt / TDCP_rijndael). The PHP script files are saved in ANSI encoding, so that the strings being used are ANSI strings, compatible with Delphi 7. (Should be!) However the Delphi decryption is producing the wrong result, I am guessing that something is wrong in the code. I would be grateful if you could have a look, and spot my error on the Delphi side: PHP Code: function encrypt($key, $payload) { $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc')); $encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv); return base64_encode($encrypted . '::' . $iv); } function decrypt($key, $garble) { list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2); return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv); } Delphi code: var DCP_rijndael: TDCP_rijndael; const cPASSWORD = 'myownpassword'; function Decrypt(AStr: string): string; var d, s, iv: String; p: Integer; begin d := Base64DecodeStr(AStr); p := Pos('::', d); s := Copy(d, 1, p - 1); iv := Copy(d, p + 2, Length(s)); DCP_rijndael.SetIV(iv); Result := DCP_rijndael.DecryptString(s); end; initialization DCP_rijndael := TDCP_rijndael.Create(nil); DCP_rijndael.Algorithm := 'Rijndael'; DCP_rijndael.CipherMode := cmCBC; //DCP_rijndael.BlockSize := 128; {tried various values with no luck!} //DCP_rijndael.MaxKeySize := 256;{tried various values with no luck!} DCP_rijndael.Init(cPASSWORD, 256, nil); finalization DCP_rijndael.Free; TIA! Edited January 20, 2021 by steve faleiro Share this post Link to post
FPiette 382 Posted January 20, 2021 Have you checked that the first stage (Base64 decoding) gives the same result? Share this post Link to post
Guest Posted January 20, 2021 38 minutes ago, steve faleiro said: However the Delphi decryption is producing the wrong result, I am guessing that something is wrong in the code. I would be grateful if you could have a look, and spot my error on the Delphi side: I am not familiar with DCPCrypt and might be wrong, but just looked at the source in GitHub while it looks a maze between i think it should not be used like that, i mean IV have the same weight as the key in encrypting and decrypting, so with basic sense the key should be initialized with the correct IV at the same time, so what will happen if you adjusted this line to something like this ? //DCP_rijndael.SetIV(iv); DCP_rijndael.Init(cPASSWORD, 256, @IV[1]); Result := DCP_rijndael.DecryptString(s); end; Share this post Link to post
steve faleiro 1 Posted January 20, 2021 21 minutes ago, Kas Ob. said: I am not familiar with DCPCrypt and might be wrong, but just looked at the source in GitHub while it looks a maze between i think it should not be used like that, i mean IV have the same weight as the key in encrypting and decrypting, so with basic sense the key should be initialized with the correct IV at the same time, so what will happen if you adjusted this line to something like this ? //DCP_rijndael.SetIV(iv); DCP_rijndael.Init(cPASSWORD, 256, @IV[1]); Result := DCP_rijndael.DecryptString(s); end; Hi Kas, Based on your suggestion, I've changed the code to the below, but it still does not work: function Decrypt(AStr: string): string; var d, s: string; p: Integer; iv: string; begin d := Base64DecodeStr(AStr); p := Pos('::', d); s := Copy(d, 1, p - 1); iv := Copy(d, p + 2, Length(s)); //DCP_rijndael.SetIV(iv); DCP_rijndael.Init(cPASSWORD, 256, @iv[1]); Result := DCP_rijndael.DecryptString(s); end; initialization DCP_rijndael := TDCP_rijndael.Create(nil); DCP_rijndael.Algorithm := 'Rijndael'; DCP_rijndael.CipherMode := cmCBC; //DCP_rijndael.BlockSize := 128; //DCP_rijndael.MaxKeySize := 256; finalization DCP_rijndael.Free; Share this post Link to post
steve faleiro 1 Posted January 20, 2021 (edited) 1 hour ago, FPiette said: Have you checked that the first stage (Base64 decoding) gives the same result? Hi Francois, Thanks for responding. In fact I'm using your excellent ICS components for implementing the Delphi REST client that is getting this data from PHP 🙂 I have now been able to verify that the data before encoding is the same on both sides - PHP and Delphi. PHP file_put_contents('c:\tmp\phpdata.txt', $encrypted . '::' . $iv); Delphi .. d := Base64DecodeStr(AStr); SaveTextToTxtFile('c:\tmp\delphi_data.txt', d); .. procedure SaveTextToTxtFile(ATxtFile, AText: String); var sl:TStringList; begin sl:=TStringList.Create; try sl.Add(AText); sl.SaveToFile(ATxtFile); finally sl.Free; end; end; This implies that my usage of DCPCrypt is wrong. Waiting on Delphi / DCPCrypt guru for resolution ! Edited January 20, 2021 by steve faleiro Share this post Link to post
aehimself 396 Posted January 23, 2021 (edited) There are 2 things you have to keep in mind: 1, IV, as @Kas Ob. pointed out. You must find out what IV your PHP encryption is using (if any). Afaik, DCPCrypt will use the part of the key as an IV, if not given any but I might be wrong here. 2, Character encoding. Delphi 7 is ANSI, and a web interface is usually UTF-8. So you take an UTF-8 string, encrypt and Base64 it. If you unbase64 and decrypt it you still have to do an encoding conversion, otherwise the result will be not a valid string for Delphi. What I advise at first: check the raw text and the encrypted binary (before Base64-ing it) with a hex viewer. Then, check the received binary (after unbase64ing it) and the uncompressed "text" with a hex viewer; that can already give you a hint. I personally abandoned the EncryptString / DecriptString a long time ago. I'm converting strings to TBytes (Array Of Byte) and performing the encryption on the binary data. Edit: I was wrong about using the key as IV. It just generates one for itself: procedure TDCP_blockcipher64.Init(const Key; Size: longword; InitVector: pointer); begin inherited Init(Key,Size,InitVector); InitKey(Key,Size); if InitVector= nil then begin FillChar(IV,8,{$IFDEF DCP1COMPAT}$FF{$ELSE}0{$ENDIF}); EncryptECB(IV,IV); Reset; end else begin Move(InitVector^,IV,8); Reset; end; end; Edited January 23, 2021 by aehimself Share this post Link to post
Guest Posted January 24, 2021 9 hours ago, aehimself said: FillChar(IV,8,{$IFDEF DCP1COMPAT}$FF{$ELSE}0{$ENDIF}); I wouldn't suggest doing that, no library should be in indeterministic state or have different IV based on the build. Also i have no idea what DCP1COMPAT should do, but that usage is far from right, you know that the IV is not secret by design, meaning it can be disclosed with encrypted data, but the user of the library initialized the enc/dec process by calling above Init on the Key with an empty IV (=nil), here the compilation directive could simply render the IV as unexpected value (filled with $FF) instead of simply zeroes. Share this post Link to post