Search code examples
phpdelphibase64delphi-7php-7.4

Delphi 7 - Encrypt with DEC, and decrypt with PHP OpenSSL (Part II)


Please refer to my earlier question: Delphi 7 - Encrypt with DEC, and decrypt with PHP OpenSSL

My Delphi 7 client app (ICS components) is sending base64 encoded data as a parameter in a HTTP POST request, to a PHP web service, where it is being decoded with base64_decode function. However, the resulting decoded string in PHP is not the same string that was originally encoded in Delphi. The Delphi base64 encoding function that I'm using is from the DEC (DEC v5.2) library.

Sample code implementation for sending data from Delphi to PHP (and receiving a response) is below:

Delphi side:

uses
  OverbyteIcsHttpProt, OverbyteIcsLogger, OverbyteIcsWSocket, OverbyteIcsSslHttpRest, StrUtils,
  DecFmt;


var
  d: String;
  StatCode: Integer;
  sAPIResponse: WideString;
  APIRespData: TAPIRespData;
begin
  //
  d := 'The quick brown fox jumps over the lazy rabbit..'; 
  d := Encrypt(d); // Result string A
  d := TFormat_MIME64.Encode(d); // Result string B

  SslHttpRest1.RestParams.Clear;
  SslHttpRest1.RestParams.AddItem('data', d);
  StatCode := SslHttpRest1.RestRequest(httpPOST, strAPIUrl, False, '');
  sAPIResponse := SslHttpRest1.ResponseRaw; // Result string C

Encrypt looks like this (Code credit: @AmigoJack):

const
  GLUE = '::';
  cPASSWORD = 'myownpassword';

function Encrypt(AStr: string): string;
var
  c: TDecCipher; // Successfully tested with DEC 5.2 on Delphi 7
  sKey, // The binary key we have to provide
  sIv, // Initialization vector, should be random in the real world
  sEncrypted, // Output of the encryption
  sPlain: AnsiString; // What we want to encrypt, in binary
  iPlus, // Input data padding
  iLength: Integer; // Plaintext length source, in bytes
begin
  // Keep in mind: Plain, Key and IV are all binary, not text!
  sPlain := AStr;
  sKey := cPASSWORD;

  SetLength(sIv, 16);
  RandomBuffer(sIv[1], 16);

  // The cipher/algorithm depends on fixed block sizes, so it is automatically
  // padded to the next full length. OpenSSL's padding byte is equal to the amount
  // of bytes to be added: if 6 bytes need to be added, then 6 times #6 is added.
  iLength := Length(sPlain);
  iPlus := 16 - (iLength mod 16);
  sPlain := sPlain + StringOfChar(Chr(iPlus), iPlus);

  // Expect DEC 5.2 to only deal with AES-128-CBC, not 256.
  c := ValidCipher(DecCipher.TCipher_Rijndael).Create;
  try
    c.Mode := cmCBCx;
    c.Init(sKey, sIv); // Provide binary key and binary IV
    SetLength(sEncrypted, Length(sPlain)); // Both are multiples of 16
    c.Encode(sPlain[1], sEncrypted[1], Length(sPlain));

    Result := TFormat_MIME64.Encode(sEncrypted) + GLUE + TFormat_MIME64.Encode(sIv) + GLUE + IntToStr(iLength);
  finally
    c.Free;
  end;
end;

Result string A:

gS4S0kX9BfrhBYlsZ3MHS9RdgNuuEmKub39aim4/0eiRBPiaw1ykOGOqEk6cY3ol::AAjcM0WrUSlfbBR5EtcPSw==::48

Result string B:

a1FUTWtyVFlNVWc1UlREL2lEOXVPaTZUcmtDaHRiMUFLWU9DRVRxODUvQVNpZkJzMEs2Y2xxMU50VGZCNjdOYzo6Nmw3R1U3TFl0MDRwVkhjLzAwZDdKZz09Ojo0OA==

Result string C:

B:a1FUTWtyVFlNVWc1UlREL2lEOXVPaTZUcmtDaHRiMUFLWU9DRVRxODUvQVNpZkJzMEs2Y2xxMU50VGZCNjdOYzo6Nmw3R1U3TFl0MDRwVkhjLzAwZDdKZz09Ojo0OA== A:kQTMkrTYMUg5RTD/iD9uOi6TrkChtb1AKYOCETq85/ASifBs0K6clq1NtTfB67Nc::6l7GU7LYt04pVHc/00d7Jg==::48

PHP side:

$data=$_POST['data'];          echo 'B:' . $data;
$data=base64_decode($data);    echo ' A:' . $data;

I am expecting String A to be equal to the string returned in String C (label A:), but they aren't.

Imho, the bug relates to either something missing relating to character set encoding, or, an incompatibility between DEC's TFormat_MIME64.Encode function and PHP (PHP v7.4.7) base64_decode function.

Any help in resolving this or pointing me in the right direction is appreciated!


Solution

  • This can't be true - "Result string B" cannot become what you stated. Are you sure you use DEC5.2 and not an older version?

    var
      sAll, sAgain: AnsiString;
    begin
      sAll:= 'gS4S0kX9BfrhBYlsZ3MHS9RdgNuuEmKub39aim4/0eiRBPiaw1ykOGOqEk6cY3ol::AAjcM0WrUSlfbBR5EtcPSw==::48';
      sAgain:= TFormat_MIME64.Encode( sAll );
      if sAgain<> 'Z1M0UzBrWDlCZnJoQllsc1ozTUhTOVJkZ051dUVtS3ViMzlhaW00LzBlaVJCUGlhdzF5a09HT3FFazZjWTNvbDo6QUFqY00wV3JVU2xmYkJSNUV0Y1BTdz09Ojo0OA==' then Halt;
    

    Also it is totally pointless to encode your ASCII string again in Base64 - you just inflate it even more without any benefit.