0

as in the title, I need to implement in my C# code the equivalent of php's openssl_encrypt method, because I need to call a service on a php page, but we work with c#. The php code is this:

    $textToEncrypt = "test";                                        
  
    $algo = "AES256";
    $iv   = openssl_random_pseudo_bytes(openssl_cipher_iv_length($algo));
    $key  = "1234567890987654"; //Not this key, but just same length
    $parametri_enc  = openssl_encrypt($textToEncrypt , $algo, $key, 0, $iv);
    $iv   = bin2hex($iv); 

I tried many thing, actually my code is:

    string textToEncrypt = "test";
    string secretCode = "1234567890987654"

    // Create sha256 hash
    SHA256 mySHA256 = SHA256Managed.Create();
    byte[] key = mySHA256.ComputeHash(Encoding.ASCII.GetBytes(secretCode));

    // Create secret IV
    byte[] iv = new byte[16];
    RandomNumberGenerator generator = RandomNumberGenerator.Create();
    generator.GetBytes(iv);

    string encryptedText = EncryptString(textToEncrypt, key, iv);

    // And I try to port also the bin2hex method
    var sb = new StringBuilder();
    foreach (byte b in iv)
    {
       sb.AppendFormat("{0:x2}", b);
    }
    var tokenBytesHex = sb.ToString();

And the method EncryptString is

public static string EncryptString(string plainText, byte[] key, byte[] iv)
{
   //Instantiate a new Aes object to perform string symmetric encryption
   Aes encryptor = Aes.Create();

   encryptor.Mode = CipherMode.CBC;

   // Set key and IV
   byte[] aesKey = new byte[32];
   Array.Copy(key, 0, aesKey, 0, 32);
   encryptor.Key = aesKey;
   encryptor.IV = iv;

   // Instantiate a new MemoryStream object to contain the encrypted bytes
   MemoryStream memoryStream = new MemoryStream();

   // Instantiate a new encryptor from our Aes object
   ICryptoTransform aesEncryptor = encryptor.CreateEncryptor();

   // Instantiate a new CryptoStream object to process the data and write it to the 
   // memory stream
   CryptoStream cryptoStream = new CryptoStream(memoryStream, aesEncryptor, CryptoStreamMode.Write);

   // Convert the plainText string into a byte array
   byte[] plainBytes = Encoding.ASCII.GetBytes(plainText);

   // Encrypt the input plaintext string
   cryptoStream.Write(plainBytes, 0, plainBytes.Length);

   // Complete the encryption process
   cryptoStream.FlushFinalBlock();

   // Convert the encrypted data from a MemoryStream to a byte array
   byte[] cipherBytes = memoryStream.ToArray();

   // Close both the MemoryStream and the CryptoStream
   memoryStream.Close();
   cryptoStream.Close();

   // Convert the encrypted byte array to a base64 encoded string
   string cipherText = Convert.ToBase64String(cipherBytes, 0, cipherBytes.Length);

   // Return the encrypted data as a string
   return cipherText;
}

I tried many variation about this porting (that I've found on internet), but without result. If I use a correct encrypted string from my code, I can call the service, so it is working. I need only to encrypt correctly that string, but until now, I've failed

  • 1
    If you use an AES-256 key whose last 16 bytes are fixed 0x00 values, the security is equivalent to AES-128. – Topaco Mar 08 '22 at 11:44
  • @Topaco I see. But if the php code use the same key, it should be the same also for them, isn't? For this I tried the code below, where the key is simply padded to 32 bit. It should be the same in the php openssl method, if you use a short key, it should do the same (I think). Basically the code below is working, I don't know if it is possibile to improve that code or not – F. Pelliccia Mar 08 '22 at 12:08
  • 1
    I'm just pointing out a vulnerability of the PHP code, which is that the AES variant and the key size don't match. This is not how it should be done (for those who want to use this PHP code). – Topaco Mar 08 '22 at 12:14

1 Answers1

0

Ok i solved my own problem. I'll share it so if anyone has the same problem, this could work. Basically I saw a decryption c# code here so I update my code in this way First, I pass my secretCode in string format instead of byte[]

So i changed my method signature in this way:

public static string EncryptString(string plainText, string secretcode, byte[] iv)

and inside I changed the way I manipulate the secretCode (passphrase in php equivalent method)

// Set key and IV
var aesKey = Encoding.ASCII.GetBytes(secretcode);

//pad key out to 32 bytes (256bits) if its too short
if (aesKey.Length < 32)
{
   var paddedkey = new byte[32];
   Buffer.BlockCopy(aesKey, 0, paddedkey, 0, aesKey.Length);
   aesKey = paddedkey;
}

So it worked! No other change, just this two small change from my previous post

Updated method

public static string EncryptString(string plainText, string secretcode, byte[] iv)
{
   // Instantiate a new Aes object to perform string symmetric encryption
   Aes encryptor = Aes.Create();

   encryptor.Mode = CipherMode.CBC;

   // Set key and IV
   var aesKey = Encoding.ASCII.GetBytes(secretcode);

   //pad key out to 32 bytes (256bits) if its too short
   if (aesKey.Length < 32)
   {
      var paddedkey = new byte[32];
      Buffer.BlockCopy(aesKey, 0, paddedkey, 0, aesKey.Length);
      aesKey = paddedkey;
   }

   encryptor.Key = aesKey;
   encryptor.IV = iv;

   // Instantiate a new MemoryStream object to contain the encrypted bytes
   MemoryStream memoryStream = new MemoryStream();

   // Instantiate a new encryptor from our Aes object
   ICryptoTransform aesEncryptor = encryptor.CreateEncryptor();

   // Instantiate a new CryptoStream object to process the data and write it to the 
   // memory stream
   CryptoStream cryptoStream = new CryptoStream(memoryStream, aesEncryptor, CryptoStreamMode.Write);

   // Convert the plainText string into a byte array
   byte[] plainBytes = Encoding.ASCII.GetBytes(plainText);

   // Encrypt the input plaintext string
   cryptoStream.Write(plainBytes, 0, plainBytes.Length);

   // Complete the encryption process
   cryptoStream.FlushFinalBlock();

   // Convert the encrypted data from a MemoryStream to a byte array
   byte[] cipherBytes = memoryStream.ToArray();

   // Close both the MemoryStream and the CryptoStream
   memoryStream.Close();
   cryptoStream.Close();

   // Convert the encrypted byte array to a base64 encoded string
   string cipherText = Convert.ToBase64String(cipherBytes, 0, cipherBytes.Length);

   // Return the encrypted data as a string
   return cipherText;
}
  • I'm guessing PHP actually uses UTF8 not ASCII. Also you are missing a bunch of `using` blocks – Charlieface Mar 08 '22 at 13:09
  • I tried with UTF8 but it didn't worked. I switched to ASCII for this reason (and now it is working). About using, you are right. One is on memorystream, I guess. The other one should be Cryptostream (both implement IDispose interface). I should also delete both the .Close method at the end, if I use the using declarement, right? – F. Pelliccia Mar 08 '22 at 16:23
  • UTF8 and ASCII will be almost the same for Latin characters. Yes, you don't need `Close()` if you have a `using`. But `Aes` and `ICryptoTransform` also implement `IDisposable` and need `using`. – Charlieface Mar 08 '22 at 16:27