How do I decrypt in php something aes encrypted with c# - c#

A partner is using c# to encrypt 12 digit numbers to a database, I need to decode these values on a unix server ideally using php 5.6 but php 7.2, perl 5.16 or python 3.6 are all available.
I've started with example 2 of the php openssl-encrypt manual which gets me something which will encode/decode test strings successfully, but cannot decode the c# encoded data.
I've also tried the suggestions at https://odan.github.io/2017/08/10/aes-256-encryption-and-decryption-in-php-and-csharp.html without success, presumably because the partner isn't using CBC.
Substituting $key = substr(hash('sha256', $secret, true), 0, 32); for the secret in the function calls didn't help.
I've tried all the ciphers supported by openssl-encrypt without success.
The partner has provided their c# code (which appears to be a more or less direct lift from Microsoft sample code):
public static string EncryptString(string key, string plainText)
{
byte[] iv = new byte[16];
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
{
streamWriter.Write(plainText);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
public static string DecryptString(string key, string cipherText)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
I have the following functions at present:
function encrypt2($data, $secret, $cipher, $iv)
{
$ivlen = openssl_cipher_iv_length($cipher);
$ciphertext_raw = openssl_encrypt($data, $cipher, $secret, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $secret, $as_binary=true);
return base64_encode($ciphertext_raw);
}
function decrypt2($data, $secret, $cipher, $iv)
{
$c = base64_decode($data);
$ivlen = openssl_cipher_iv_length($cipher);
$original_plaintext = openssl_decrypt($c, $cipher, $secret, $options=OPENSSL_RAW_DATA, $iv);
return ($original_plaintext ? $original_plaintext : 'openssl_decrypt failed');
}
I can't share partner data because I only have real data but here is an example call to the php functions above the second of which produces results resembling the real data:
$data = '123456789012';
$secret = "qwertyuiop12345";
$cipher = "AES-256-ECB";
$iv = "";
$encrypted2 = encrypt2($data, $secret, $cipher, $iv);
$decrypted2 = decrypt2($encrypted2, $secret, $cipher, $iv);
echo "$data > $encrypted2 > $decrypted2\n";
Gives:
123456789012 > NDdefqxcYZvtjAcvPrQ00A== > 123456789012
What changes do I need to make to my php to match the c# code?
Alternatively a solution using perl or python would work.
Update with sample data:
xyz#gmail.com encrypted with b14ca5898a4e4133bbce2ea2315a1916 gives c4Eej4S8G3mclVQJb+qkfQ==
That's exactly what my code generates.
The live key I've been given is 16 characters long but the example above uses a 32 character string, is something being done to double the length of the original before use?

Related

IV of first 16 bytes gets remove from decrypted string? C#/Python3

I was wondering why the first 16 bytes of all my strings being encrypted, then when being decrypted are missing and how to fix this if it is possible. I am encrypting like so in c#
public static string EncryptString(string b_key, string plainText)
{
byte[] iv = new byte[16];
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(b_key);
aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
{
streamWriter.Write(plainText);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
and decrypting in python3 like so
enc = base64.b64decode(self.text)
iv = enc[:16]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
plain_text = cipher.decrypt(enc[16:])
plain_text = self.dePKCS7_padding(plain_text)
return plain_text
Is readding the first 16 bytes possible? or must be used for encryption. I also want it to crypto safe but the first 16 bytes are kind of important is this possible? anyway to get around this in either c# or python3?
Based on the discussion in comments and inputs from #MichaelFehr and #user9014097, I came up with the following code.
In this code the IV of AES will have random value created when AES.Create() is called. And the same will be used in the outcome of the encrypted value.
The decryptString method will capture the iv value from the incoming encrypted string and assign it to AES while decrypting the string.
public static string EncryptString(string b_key, string plainText)
{
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(b_key);
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
// Adding aes.IV to the stream's start.
memoryStream.Write(aes.IV);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write(plainText);
}
}
array = memoryStream.ToArray();
}
}
// The final encrypted outcome will be aes.IV+encryptedtext.
return Convert.ToBase64String(array);
}
public static string DecryptString(string key, string cipherText)
{
//input is iv+encrypted text, convert them to byte array.
byte[] buffer = Convert.FromBase64String(cipherText);
// byte array for iv
byte[] iv = new byte[16];
// byte array for rest of the cipher text.
byte[] cipherBuffer = new byte[buffer.Length - 16];
// copy first 16 bytes from the cipher text to iv.
Buffer.BlockCopy(buffer, 0, iv, 0, 16);
// copy rest of the cipher text to the cipher buffer to be decrypted.
Buffer.BlockCopy(buffer, 16, cipherBuffer, 0, buffer.Length - 16);
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(cipherBuffer))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader(cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
I have following assumption in writing above code.
Length of IV is 16.
Python code (shared above) does not need split the input text based on some specific character. It takes first 16 bytes as IV value and rest of the bytes as cipher text.
I was able to encrypt and decrypt values successfully in C# using above methods.
I was not able to decrypt the value in python code as I have little to no idea on how to work with python.
You can test the outcome of above encryption in python to decrypt it. Let me know if it doesn't work as expected.
I hope this will help you solve your issue.

TripleDES Encryption between PHP vs C#/.NET

I have legacy server uses TripleDES encryption in .NET/C#.
Need to decrypt text by using PHP.
I wrote PHP code but it's not able to decrypt message generated form C#.
C# Code
using System;
using System.Data;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace testns
{
class Program
{
static void Main(string[] args)
{
string key = "123456789012345678901234";
string iv = "12345678";
string text = "this is just test string";
string e = Program.EncryptTripleDES(text, key, iv);
Console.WriteLine(e);
string d = Program.DecryptTripleDES(e, key, iv);
Console.WriteLine(d);
// Return
// QDIRAeQ/O1hhjN4XqgcETG7IChnybCqZ
// this is just test string
}
private static string EncryptTripleDES(string neqs, string nafKeyCode, string nafIvCode)
{
byte[] rgbKey = Encoding.UTF8.GetBytes(nafKeyCode);
byte[] rgbIV = Encoding.UTF8.GetBytes(nafIvCode);
string sEncrypted = string.Empty;
if (!String.IsNullOrEmpty(neqs))
{
TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();
cryptoProvider.Mode = CipherMode.CBC;
cryptoProvider.Padding = PaddingMode.None;
byte[] buffer = Encoding.UTF8.GetBytes(neqs);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, cryptoProvider.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
cs.Write(buffer, 0, buffer.Length);
cs.FlushFinalBlock();
sEncrypted = Convert.ToBase64String(ms.ToArray());
}
return sEncrypted;
}
private static string DecryptTripleDES(string neqs, string nafKeyCode, string nafIvCode)
{
byte[] rgbKey = Encoding.UTF8.GetBytes(nafKeyCode);
byte[] rgbIV = Encoding.UTF8.GetBytes(nafIvCode);
string decryptedText = string.Empty;
if (!String.IsNullOrEmpty(neqs))
{
TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();
cryptoProvider.Mode = CipherMode.CBC;
cryptoProvider.Padding = PaddingMode.None;
byte[] buffer = Convert.FromBase64String(neqs);
MemoryStream ms = new MemoryStream(buffer);
CryptoStream cs = new CryptoStream(ms, cryptoProvider.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Read);
StreamReader sr = new StreamReader(cs);
decryptedText = sr.ReadToEnd();
//(new Logs()).LogException(decryptedText);
}
return decryptedText;
}
}
}
PHP Code
$key = '123456789012345678901234';
$iv = '12345678';
$text = 'this is just test string';
$e = openssl_encrypt($text, 'des-ede3-cbc', $key, 0, $iv);
echo $e . "<br /><br />";
$d = openssl_decrypt($e, 'des-ede3-cbc', $key, 0, $iv);
echo $d . "<br /><br />";
// Return
// QDIRAeQ/O1hhjN4XqgcETG7IChnybCqZqN3DpVbYFwk=
// this is just test string
Got from PHP
QDIRAeQ/O1hhjN4XqgcETG7IChnybCqZqN3DpVbYFwk=
Got from C#
QDIRAeQ/O1hhjN4XqgcETG7IChnybCqZ
As you an see it's almost the same for PHP has extra qN3DpVbYFwk= characters.
What I am doing wrong? It is something to do with padding?
Thanks
It looks like the problem is that you have turned padding off (PaddingMode.None) in your C# code and Padding is turned on (by default) in your PHP code.
The OpenSSL library methods openssl_encrypt and openssl_decrypt have padding turned on by default when you pass 0 as the options parameter. The default padding is PKCS#7.
So to solve your issue you will either need to add PaddingMode.PKCS7 to your C# code (which I personally recommend):
cryptoProvider.Padding = PaddingMode.PKCS7;
Or you turn off the padding in PHP using OPENSSL_ZERO_PADDING. Remember that in PHP you will need to add the flag OPENSSL_ZERO_PADDING to both openssl_encrypt and openssl_decrypt.
example:
$e = openssl_encrypt($text, 'des-ede3-cbc', $key, OPENSSL_ZERO_PADDING, $iv);
Important
Remember the padding options must be set on both encrypt and decrypt modes.

Convert PHP Encryption algorithm to C#

I am trying to use a SSO solution in C#, where the documentation is only available in PHP.
I have this PHP Code:
function encrypt ($message)
{
$initialVector = "1234567890123456";
$key = md5($this->apiPassword);
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $message, MCRYPT_MODE_CFB, $initialVector);
return base64_encode($initialVector) .":" . base64_encode($crypt);
}
The C# Code I tried is the following:
private string encrypt(string message)
{
RijndaelManaged aes128 = new RijndaelManaged();
aes128.BlockSize = 128;
aes128.KeySize = 128;
aes128.Mode = CipherMode.CFB;
aes128.Padding = PaddingMode.None;
aes128.IV = Encoding.ASCII.GetBytes("1234567890123456");
aes128.Key = Encoding.ASCII.GetBytes(getMd5(key));
byte[] plainTextBytes = Encoding.ASCII.GetBytes(json);
ICryptoTransform encryptor = aes128.CreateEncryptor();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
cs.Write(plainTextBytes, 0, plainTextBytes.Length);
// convert our encrypted data from a memory stream into a byte array.
byte[] cypherTextBytes = ms.ToArray();
// close memory stream
ms.Close();
return Convert.ToBase64String(aes128.IV) + ":" + Convert.ToBase64String(cypherTextBytes);
}
key and message are identical. The IV part is returned correctly, only the encrypted parts are not equal. The md5 method is also working correctly.
Edit: Changing the Padding also doesn't change anything.

PHP C# MCRYPT_RIJNDAEL_128

I try to do encrypt (with MCRYPT_RIJNDAEL_128 Cipher CBC) in PHP and C#, but the output does not match.
key = '1234567812345678';
iv = '1234567812345678';
PHP:
function Encrypt($data, $key, $iv) {
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, base64_encode($data), MCRYPT_MODE_CBC, $iv));
}
C#:
public static string Encrypt(string plainText, string key, string iv)
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(iv);
byte[] plainTextBytes = Encoding.ASCII.GetBytes(plainText);
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.Zeros;
symmetricKey.KeySize = 128;
symmetricKey.BlockSize = 128;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
Help appreciated.
I think your problems lies within your PHP code.
The mcrypt_encrypt function expects the data (which needs to be encoded) to be in plaintext format, so you don't need an additional base64_encode since you are working with strings. The same way you pass the key and the iv to the function, you should hand your data variable to it. (untested)
function Encrypt($data, $key, $iv) {
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv));
}
Let me know your progress, so that I can help you with this in the future if anything doesn't work :)

How can I get CSharp / .NET to "play nice" with PHP-encrypted strings?

(preface: I'm a newbie with encryption and security, figured this would be a fun way to learn)
I'm building a program in C# that communicates with a server written in PHP using standard HTTP protocol. I want both programs to be able to send and receive encrypted data. However, there appear to be inconsistencies regarding how the encryption is being handled, despite them both using the same kind of functions.
Both programs use Rjindael 128 bit in CBC mode.
For demonstration / testing, I made two functions that are virtually identical, each taking the same string, encrypting it, and spitting out the result as a base64 string.
The PHP function:
public static function EncryptionTest () {
echo 'Testing Encryption to base64 string...<br/>';
$originalString = 'This is the original String! How cool is that?';
$key = "abcdefghijklmnopqrstuvwxyz012345";
$iv = "1234567890123456";
$encrypted = mcrypt_encrypt (MCRYPT_RIJNDAEL_128, $key, $originalString, MCRYPT_MODE_CBC, $iv);
echo base64_encode ($encrypted);
}
And it's C# counterpart:
public static void EncryptionTest ()
{
System.Console.WriteLine ("Testing Encryption to base64 string...");
string originalString = "This is the original String! How cool is that?";
byte [] encryptedData;
byte [] key = System.Text.ASCIIEncoding.UTF8.GetBytes ("abcdefghijklmnopqrstuvwxyz012345");
byte [] iv = System.Text.ASCIIEncoding.UTF8.GetBytes ("1234567890123456");
RijndaelManaged cryptor = new RijndaelManaged ();
cryptor.Key = key;
cryptor.IV = iv;
cryptor.BlockSize = 128;
cryptor.Mode = CipherMode.CBC;
ICryptoTransform encryptor = cryptor.CreateEncryptor (cryptor.Key, cryptor.IV);
using (MemoryStream msEncrypt = new MemoryStream ())
{
using (CryptoStream csEncrypt = new CryptoStream (msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter (csEncrypt) )
{
swEncrypt.Write (originalString);
}
encryptedData = msEncrypt.ToArray ();
}
}
System.Console.WriteLine (System.Convert.ToBase64String (encryptedData) );
}
Now, here is the PHP result:
Testing Encryption to base64 string...
yzwIdowhLj+cMOFPMuHSA80pWQ6R8yfFQlEsLx5kIzUOJdFykLjsaKfK4VfaBGRv
And the C# result:
Testing Encryption to base64 string...
yzwIdowhLj+cMOFPMuHSA80pWQ6R8yfFQlEsLx5kIzUg74mGEQf9iW+OQ68m6cpp
As you can see, the two results are clearly encrypted (good), are the same number of characters (good), but are different (could be bad).
I wrote decryption tests which take these strings and process them. Here is the PHP function:
public static function DecryptionTest () {
$phpBase64 = 'yzwIdowhLj+cMOFPMuHSA80pWQ6R8yfFQlEsLx5kIzUOJdFykLjsaKfK4VfaBGRv';
$csBase64 = 'yzwIdowhLj+cMOFPMuHSA80pWQ6R8yfFQlEsLx5kIzUg74mGEQf9iW+OQ68m6cpp';
$key = "abcdefghijklmnopqrstuvwxyz012345";
$iv = "1234567890123456";
$phpEncrypted = base64_decode ($phpBase64);
$phpDecrypted = mcrypt_decrypt (MCRYPT_RIJNDAEL_128, $key, $phpEncrypted, MCRYPT_MODE_CBC, $iv);
$csEncrypted = base64_decode ($csBase64);
$csDecrypted = mcrypt_decrypt (MCRYPT_RIJNDAEL_128, $key, $csEncrypted, MCRYPT_MODE_CBC, $iv);
echo 'Decrypted PHP string: "' . $phpDecrypted . '"<br/>' .
'Decrypted CS string: "' . $csDecrypted . '"';
}
And the C# version:
public static void DecryptionTest ()
{
System.Console.WriteLine ("Testing Decryption for PHP and CS generated base64 strings!");
string phpBase64 = "yzwIdowhLj+cMOFPMuHSA80pWQ6R8yfFQlEsLx5kIzUOJdFykLjsaKfK4VfaBGRv";
string csBase64 = "yzwIdowhLj+cMOFPMuHSA80pWQ6R8yfFQlEsLx5kIzUg74mGEQf9iW+OQ68m6cpp";
byte [] phpEncrypted = System.Convert.FromBase64String (phpBase64);
byte [] csEncrypted = System.Convert.FromBase64String (csBase64);
string phpDecrypted;
string csDecrypted;
byte [] encryptedData;
byte [] key = System.Text.ASCIIEncoding.UTF8.GetBytes ("abcdefghijklmnopqrstuvwxyz012345");
byte [] iv = System.Text.ASCIIEncoding.UTF8.GetBytes ("1234567890123456");
RijndaelManaged cryptor = new RijndaelManaged ();
cryptor.Key = key;
cryptor.IV = iv;
cryptor.BlockSize = 128;
cryptor.Mode = CipherMode.CBC;
ICryptoTransform decryptor = cryptor.CreateDecryptor (cryptor.Key, cryptor.IV);
using (MemoryStream msDecrypt = new MemoryStream (csEncrypted))
{
using (CryptoStream csDecrypt = new CryptoStream (msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader swDecrypt = new StreamReader (csDecrypt) )
{
csDecrypted = swDecrypt.ReadToEnd ();
}
}
}
System.Console.WriteLine ("Decrypted CS string: \"" + csDecrypted + "\"");
using (MemoryStream msDecrypt = new MemoryStream (phpEncrypted))
{
using (CryptoStream csDecrypt = new CryptoStream (msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader swDecrypt = new StreamReader (csDecrypt) )
{
phpDecrypted = swDecrypt.ReadToEnd ();
}
}
}
System.Console.WriteLine ("Decrypted PHP string: \"" + phpDecrypted + "\"");
}
The PHP results:
Decrypted PHP string: "This is the original String! How cool is that?"
Decrypted CS string: "This is the original String! How cool is that?"
And the C# results:
Decrypted CS string: "This is the original String! How cool is that?"
CryptographicException: Bad PKCS7 padding. Invalid length 0.
at Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException (PaddingMode padding, Int32 length, Int32 position) [0x0005c] in > /Applications/buildAgent/work/84669f285f6a667f/mcs/class/corlib/Mono.Security.Cryptography/SymmetricTransform.cs:363
So, basically, the PHP code can successfully decrypt both base64 strings, but the C# code can only decrypt base64 strings that were created by its own decryptor.
A lot of this is code I found on the internet and modified to suit my needs. Like I said, I'm a newbie with cryptography, but I've gotten decently far here. I could theorize and test all day, but it's starting to eat into my schedule so I'm looking for insights from others as to why it isn't working. Thank you!
You need to use the PHPSecLib because the PHP implementation is not compatible with C++/C#. It has a different padding size (not a crypto expert here but this is what I found after days of testing). So use the native implementations in the phpseclib and it will work.
Bumped into this issue a while ago myself. But with C++ / CryptoAPI and PHP.

Categories

Resources