0

I have code in C# which encrypts and decrypts string:

private readonly UTF8Encoding _encoder;
private readonly ICryptoTransform _encryptor;
private readonly RijndaelManaged _rijndael;

public Crypter()
{
    _rijndael = new RijndaelManaged { Key = { 1, 2, 3, 4, ..., 16 } };
    _rijndael.GenerateIV();
    _encryptor = _rijndael.CreateEncryptor();
    _encoder = new UTF8Encoding();
}

public string Encrypt(string unencrypted)
    => Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted)));
        
private byte[] Encrypt(byte[] buffer)
{
    byte[] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
    return _rijndael.IV.Concat(inputBuffer).ToArray();
}

public string Decrypt(string encrypted)
    => _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
        
private byte[] Decrypt(byte[] buffer)
{
    byte[] iv = buffer.Take(16).ToArray();
    using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv))
    {
        return decryptor.TransformFinalBlock(buffer, 16, buffer.Length - 16);
    }
}

If you check Decrypt(byte[] buffer), I take first 16 bytes which is IV.

Now I similar want to implement in PHP (imagine, that I will encode on C# side and send it to server which runs on php, where I want to decrypt it back). As the param to my PHP decrypt function will be output of C# public string Encrypt(string unencrypted). I somehow need to get those 16 bytes to get IV and the rest part, which I respectively will pass as $data and $iv params to $decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, 0, $iv); function

I have tried to use something like this (using unpack):

$stringValue = base64_decode($encrypted_data, true);
$integers = unpack("s*", $stringValue);

and then tried to take 16 first numbers and somehow convert them back with pack method. But probably I have lack of knowledge.

Could you please help me with this?

P.S. This one I have tried based on Ilya's answer and comments.

$cipher = "aes-256-cbc";
$encryption_key = hex2bin(env("ENCRYPTION_KEY"));
$base64decoded = base64_decode($encrypted_data, true);
$iv = substr($base64decoded, 0, 16);
$data = substr($base64decoded, 16, strlen($base64decoded) - 16);
$decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, OPENSSL_RAW_DATA, $iv);
dd($decrypted_data);

also if I debug code and check which bytes are in $iv using this code:

$stringValue = base64_decode($iv, true);
$integers = unpack("C*", $encrypted_data);

and comparing with C# this byte array byte[] iv = buffer.Take(16).ToArray(); , they are equals, then I expect, that I am using wrongly openssl_decrypt method

Aleksandrs
  • 1,488
  • 1
  • 14
  • 34
  • You seem to use a 16 bytes key in the C# code. Then you have to use aes-128-cbc in the PHP code (otherwise PHP automatically pads with 0x00 values to 32 bytes, so that different keys are used). – Topaco Aug 02 '21 at 12:57
  • @Topaco, I have tried `$cipher = "aes-128-cbc";` but unlucky. Also I didn't understand a bit, if I use OPENSSL_RAW_DATA then I can not encode $data, correct? – Aleksandrs Aug 02 '21 at 13:09
  • Works on my machine (using your C# and PHP code and the suggested changes). How many bytes does your key have? – Topaco Aug 02 '21 at 13:11
  • `OPENSSL_RAW_DATA` must be set if you pass the raw data, which you do (since you perform a Base64 decoding before decryption). – Topaco Aug 02 '21 at 13:14
  • @Topaco 32 bytes, ok thx, then I understood correctly about OPENSSL_RAW_DATA – Aleksandrs Aug 02 '21 at 13:16
  • For a 32 bytes key, aes-256-cbc is correct. As I said, on my machine a ciphertext created with the C# code can be decrypted with the PHP code, provided the changes have been considered. If you still have problems you should post test data: plaintext, test key and ciphertext. – Topaco Aug 02 '21 at 13:21
  • it's worked!!! Before I forgot to do hex2bin(key), because key should be in binary. And then I switched back to aes-256-cbc. – Aleksandrs Aug 02 '21 at 13:22
  • @Topaco thanks a lot for your time and help. You are the best. – Aleksandrs Aug 02 '21 at 13:25

2 Answers2

1

In php any string is just a sequence of bytes, so you can work with it directly, e.g. access single byte by its index, or use substr to trim some amount of bytes. Example:

$str = 'some text or binary data received by http';
$first16Bytes = substr($str, 0, 16);
Ilia Yatsenko
  • 779
  • 4
  • 7
  • Do you mean that the `substr` returns `false`? Maybe some of previous steps returned empty string? Try to var_dump result after each step and see what went wrong. – Ilia Yatsenko Aug 02 '21 at 11:59
  • Also substr may return false when the total length of string is less than given offset. – Ilia Yatsenko Aug 02 '21 at 12:02
  • No, openssl_decrypt returns false – Aleksandrs Aug 02 '21 at 12:08
  • So the problem is not in getting first 16 bytes, right? – Ilia Yatsenko Aug 02 '21 at 12:10
  • @Aleksandrs - Code in a comment is hard to read and therefore better posted in the question. `openssl_decrypt()` expects Base64 encoded data by default. Thus, Base64 encode `$data` or set `OPENSSL_RAW_DATA` as 4th parameter in `openssl_decrypt()`. – Topaco Aug 02 '21 at 12:14
  • @IliaYatsenko, yes seems the problem is passing wrong data to `openssl_decrypt` method. – Aleksandrs Aug 02 '21 at 12:38
  • @Topaco, I have added code to my post, I have tried to pass OPENSSL_RAW_DATA, not working, as well as `base64_encode($data)` as first param of `openssl_decrypt` – Aleksandrs Aug 02 '21 at 12:40
  • @IliaYatsenko, Thanks for your answer, in the beginning I thought that the problem was in getting correctly 16 first bytes, but as we see not. But your answer is half way to resolve my issue. Thanks. – Aleksandrs Aug 02 '21 at 13:27
0

Firstly, I was having an issue how to correct retrieve 16 first bytes from string, because I thought I was doing it incorrectly. Thanks from @Ilia Yatsenko for his answer:

$first16Bytes = substr($str, 0, 16);

But then I have realised, that I am wrongly using openssl_decrypt() method. After having conversation in comments, particularly with @Topaco, we found what was the proble. Here is working code:

$cipher = "aes-256-cbc";
$encryption_key = hex2bin(env("ENCRYPTION_KEY"));
$base64decoded = base64_decode($encrypted_data, true);
$iv = substr($base64decoded, 0, 16);
$data = substr($base64decoded, 16, strlen($base64decoded) - 16);
$decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, 
OPENSSL_RAW_DATA, $iv);
dd($decrypted_data);
Aleksandrs
  • 1,488
  • 1
  • 14
  • 34