Jump to content
steve faleiro

Need help with Delphi 7 / DCPCrypt - TDCP_rijndael.DecryptString - How to make it work?

Recommended Posts

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 by steve faleiro

Share this post


Link to post
Guest
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
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
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 by steve faleiro

Share this post


Link to post

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

Share this post


Link to post
Guest
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×