PHP to Delphi and back Encryption-Decryption using Rijndael

后端 未结 2 1263
无人共我
无人共我 2020-12-17 06:55

I have problems with decrypting strings sent from PHP to Delphi using the rijndael cipher. I\'m using mcrypt on the PHP side and DCP_rijndael on the Delphi side.

At

相关标签:
2条回答
  • 2020-12-17 07:15

    Neither your PHP nor your Delphi methods appear to specify any padding. If the default paddings are different then you will get problems. Explicitly specify PKCS7 (or PKCS5) for both.

    GregS' comment about the result of decoding Base64 is correct. You are supplying encrypted cyphertext to your decRJ() method. That will be random appearing bytes. Attempting to convert it to UTF-8 will mangle it enough that it cannot be decrypted. The incoming cyphertext must be converted from Base64 direct to a byte array. Cyphertext is not a character string, which is why it needs to be converted to Base64 to be transmitted as text. It will only be text again after it has been decrypted back to plaintext.

    0 讨论(0)
  • 2020-12-17 07:32

    I seem to have spent too long on this but...

    Your problem is the block size. TDCP_rijndael is equivalent to MCRYPT_RIJNDAEL_128 (not _256). The '256' value in ciph.Init(...) call is still correct though. Other than that it looks pretty much ok. That is, assuming you're using ansistrings for key/iv or you're using non-unicode Delphi.
    For unicode Delphi versions I'd be inclined to use TBytes and key[0] / iv[0].

    Padding may still be an issue. If so, then here's what I've mangled up based on the PHP manual pages and some trial and error.

    PHP:

    function Encrypt($src, $key, $iv)
    {
      $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
      //echo "Block size: " . $block . "\r\n";
      $pad = $block - (strlen($src) % $block);
      $src .= str_repeat(chr($pad), $pad);  
    
      $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $src, MCRYPT_MODE_CBC, $iv);
      $r = base64_encode($enc);
      return $r;
    }
    
    function Decrypt($src, $key, $iv)
    {
      $enc = base64_decode($src);
      $dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv);
    
      $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
      $pad = ord($dec[($len = strlen($dec)) - 1]);
      return substr($dec, 0, strlen($dec) - $pad);
    }
    

    Delphi:

    function DecryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
    var
      key, iv, src, dest: TBytes;
      cipher: TDCP_rijndael;
      slen, pad: integer;
    begin
      //key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
      //iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
      key := TEncoding.ASCII.GetBytes(AKey);
      iv := TEncoding.ASCII.GetBytes(AIv);
    
      src := Base64DecodeBytes(TEncoding.UTF8.GetBytes(Data));
    
      cipher := TDCP_rijndael.Create(nil);
      try
        cipher.CipherMode := cmCBC;
        slen := Length(src);
        SetLength(dest, slen);
        cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES
        cipher.Decrypt(src[0], dest[0], slen);
        // Remove the padding. Get the numerical value of the last byte and remove
        // that number of bytes
        pad := dest[slen - 1];
        SetLength(dest, slen - pad);
    
        // Base64 encode it
        result := TEncoding.Default.GetString(dest);
      finally
        cipher.Free;
      end;
    end;
    
    function EncryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
    var
      cipher: TDCP_rijndael;
      key, iv, src, dest, b64: TBytes;
      index, slen, bsize, pad: integer;
    begin
      //key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
      //iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
      key := TEncoding.ASCII.GetBytes(AKey);
      iv := TEncoding.ASCII.GetBytes(AIv);
    
      src := TEncoding.UTF8.GetBytes(Data);
    
      cipher := TDCP_rijndael.Create(nil);
      try
        cipher.CipherMode := cmCBC;
        // Add padding.
        // Resize the Value array to make it a multiple of the block length.
        // If it's already an exact multiple then add a full block of padding.
        slen := Length(src);
        bsize := (cipher.BlockSize div 8);
        pad := bsize - (slen mod bsize);
        Inc(slen, pad);
        SetLength(src, slen);
        for index := pad downto 1 do
        begin
          src[slen - index] := pad;
        end;
    
        SetLength(dest, slen);
        cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES
        cipher.Encrypt(src[0], dest[0], slen);
    
        b64 := Base64EncodeBytes(dest);
        result := TEncoding.Default.GetString(b64);
      finally
        cipher.Free;
      end;
    end;
    

    The PHP and Delphi functions now give me the same answer.

    EDIT

    Base64DecodeBytes was a bit of code I added to the DCP Base64 unit:

    function Base64DecodeBytes(Input: TBytes): TBytes;
    var
      ilen, rlen: integer;
    begin
      ilen := Length(Input);
      SetLength(result, (ilen div 4) * 3);
      rlen := Base64Decode(@Input[0], @result[0], ilen);
      // Adjust the length of the output buffer according to the number of valid
      // b64 characters
      SetLength(result, rlen);
    end; 
    

    EDIT 2018 (Raising the dead...):

    As requested, here is the encoding method, unchecked and pulled straight from an old source file I found.

    DISCLAIMER: It is many years old and untested in recent memory and not used since Delphi 2010. There are probably many better alternatives now. Use at your own risk.

    function Base64EncodeBytes(Input: TBytes): TBytes;
    var
      ilen: integer;
    begin
      ilen := Length(Input);
      SetLength(result, ((ilen + 2) div 3) * 4);
      Base64Encode(@Input[0], @result[0], ilen);
    end;
    
    0 讨论(0)
提交回复
热议问题