I'm trying to convert a C# encryption function to php but I'm suspecting there is a encoding problem or the IV generation isn't correct.
This is for sending an encrypted text to an API and I've currently tried forcing utf8 but the base64 encoded strings are always different from what I get running the C# function. I also can't seem to find the exact way IV's are generated in C#.
Sadly i cannot change the way the API decrypts and I'm forced to encrypt it this way.
C# function
public void EncryptStringToBytes(string plaintext) {
string key = DateTime.UtcNow.ToShortDateString();
HashAlgorithm algorithm = SHA256.Create();
byte[] bytekey = algorithm.ComputeHash(Encoding.UTF8.GetBytes(key));
using (Aes myAes = Aes.Create()) {
myAes.Key = bytekey;
// Encrypt the string to an array of bytes.
byte[] encrypted = null;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = myAes.CreateEncryptor(myAes.Key, myAes.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream()) {
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
//Write all data to the stream.
swEncrypt.Write(plaintext);
}
encrypted = msEncrypt.ToArray();
}
}
Console.WriteLine(Convert.ToBase64String(encrypted));
Console.WriteLine(Convert.ToBase64String(myAes.IV));
}
}
PHP Function
date_default_timezone_set("UTC");
function encrypt($string) {
// Generate key based on current time
$secret_key = utf8_encode(date('Y-m-d'));
// Hash the key with SHA256
$key = hash('sha256', $secret_key);
// Use AES 128 w/ CBC as encryption method
$encrypt_method = "AES-128-CBC";
// Get cipher length based on encryption method
$cypher_length = openssl_cipher_iv_length($encrypt_method);
// Generate IV -- possible issue
$iv = openssl_random_pseudo_bytes($cypher_length);
// Encrypt input string with the given method, key and IV
$output = openssl_encrypt($string, $encrypt_method, $key, OPENSSL_RAW_DATA , $iv);
$debug_info = [ 'date' => $secret_key, 'key' => $key, 'method' => $encrypt_method, 'cypher_len' => $cypher_length, 'iv' => $iv, 'output' => $output];
return [base64_encode($output), base64_encode($iv), $debug_info];
}
I found the solution thanks to bartonjs, I had to use AES-256-CBC instead of 128. Also I wasn't using raw output when hashing which also altered the outcome and since I'm sending the encrypted data and IV trough php-soap using SoapClient I don't have to use base64_encode since SoapClient already does that for me.
The working code is as follows:
function encrypt($string, $debug = false) {
$method = 'aes-256-cbc';
$secret_key = date('Y-m-d', time());
$key = hash('SHA256', $secret_key, true);
$cypher_length = openssl_cipher_iv_length($method);
$iv = random_bytes($cypher_length);
$output = openssl_encrypt($string, $method, $key, OPENSSL_RAW_DATA, $iv);
$debug_info = [ 'date' => $secret_key, 'date_utf8' => utf8_encode($secret_key), 'key' => utf8_encode($key), 'method' => $method, 'cypher_len' => $cypher_length, 'iv' => base64_encode($iv), 'output' => base64_encode($output)];
if($debug){
return [$output, $iv, $debug_info];
}
return [$output, $iv];
}