Encrypt AES in PHP, decrypt in C# - c#

I tried to encrypt data using this function in PHP and decrypt it with the other function in C#. But I don't get the same string.
//php function
public function onCrypt($text)
{
$key=md5('DFDFDFDFDFDFDFDFDFDFDFDF',true);
$crypttext = urldecode(trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND))));
$text_crp =base64_encode($crypttext);
return $text_crp;
}
//c# function
//public static void DecryptFile
Parameters :
strKey : the key choosed in decryption .
PathPlainTextFile : path of the crypted file
PathPlainTextFile : the original file decrypted.
public static void DecryptFile(string strKey, string pathPlainTextFile, string pathCypheredTextFile)
{
//crypt key with md5 function
System.Security.Cryptography.MD5 alg = System.Security.Cryptography.MD5.Create();
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
byte[] md5val = alg.ComputeHash(enc.GetBytes(strKey));
StreamReader fsPlainTextFile = File.OpenText(pathPlainTextFile);
FileInfo t = new FileInfo(pathCypheredTextFile);
StreamWriter Tex =t.CreateText();
string input = null;
while ((input = fsPlainTextFile.ReadLine()) != null)
{
byte[] cipheredData = Convert.FromBase64String(input);
RijndaelManaged rijndaeld = new RijndaelManaged();
// define the used mode
rijndaeld.Mode = CipherMode.ECB;
// create the cipher AES - Rijndael
ICryptoTransform decryptor = rijndaeld.CreateDecryptor(md5val,null);
// Write the ciphered data in MemoryStream
MemoryStream ms= new MemoryStream(cipheredData);
CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
// Insert the ciphered data in a byte array
byte[] plainTextData = new byte[cipheredData.Length];
int decryptedByteCount = cs.Read(plainTextData, 0, plainTextData.Length);
ms.Close();
cs.Close();
// Insert the ciphered data in string encoded on Base64
Tex.WriteLine (Encoding.UTF8.GetString(plainTextData, 0, decryptedByteCount));
}
Tex.Close();
}

ECB mode is not secure. You should use either CTR mode or CBC mode. It is also best to explicitly specify the padding you are going to use at both ends.

At a quick glance, you're not supplying an IV to the C# decryptor:
ICryptoTransform decryptor = rijndaeld.CreateDecryptor(md5val, null);
I'm not familiar with php, but it looks like you created an IV when you encrypted the content. You'll need to have that same IV to decrypt it in the C# code (you'd need the same IV to decrypt it even if you were doing the decryption through php).

Related

How implement Php's openssl_encrypt method in c#?

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
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;
}

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.

Junk bytes before payload in C# UTF8 to AES to Base64 conversion

I have been trying to implement proper IV practice in methods to encrypt and decrypt a UTF-8 string with AES which is then returned as a Base64 string. Using this question as a reference, I have prepended the generated IV to the byte array before the Base64 conversion. I'm having an issue where the decrypt method returns the UTF-8 string with exactly fifty characters of random junk (encryption artifacts?). I don't believe the issue is with the encryption because the decrypt method does consistently return the encrypted string. I think the problem is with one of the other conversion steps but I'm having trouble seeing where this might be coming from. Any help would be wildly appreciated.
Encrypt method
public static string EncryptString(string input, string key)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = System.Convert.FromBase64String(key);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] rawData = Encoding.UTF8.GetBytes(input);
// IV is the 16 byte AES Initialization Vector
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
{
using (var ms = new MemoryStream())
{
ms.Write(aes.IV, 0, aes.IV.Length); // aes.IV.Length should be 16
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(rawData, 0, rawData.Length);
cs.FlushFinalBlock();
}
byte[] encryptedData = ms.ToArray();
// this will hold the IV prepended to the encrypted data
byte[] output = new byte[aes.IV.Length + encryptedData.Length];
Array.Copy(aes.IV, output, aes.IV.Length); // save the iv
Array.Copy(encryptedData, 0, output, aes.IV.Length, encryptedData.Length); // save the data
// now encode the whole thing as base 64
return System.Convert.ToBase64String(output);
}
}
}
}
Decrypt method
public static string DecryptString(string input, string key)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = Convert.FromBase64String(key);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] rawData = Convert.FromBase64String(input);
byte[] IV = new byte[16]; // aes.IV.Length should be 16
Array.Copy(rawData, IV, IV.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, IV), CryptoStreamMode.Write))
{
using (var binaryWriter = new BinaryWriter(cs))
{
binaryWriter.Write(rawData,IV.Length ,rawData.Length - IV.Length);
}
}
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
My test
static void Main(string[] args)
{
string payload = "My super secret string";
string key = "tR4mPn7mBQ8G6HWusyFnGk/gqdd/enWiUTr7YbhNrJg=";
Console.WriteLine(payload);
Console.WriteLine(key);
Console.WriteLine("");
string encrypted = EncryptString(payload, key);
Console.WriteLine(encrypted);
Console.WriteLine("");
string decrypted = DecryptString(encrypted, key);
Console.WriteLine(decrypted);
Console.WriteLine(decrypted.Length.ToString() + " " + encrypted.Length.ToString());
Console.ReadKey();
}
Edit to add - this is an example of the output:
�XQ��=F�]�D�?�My super secret string
You are writing the IV to the output twice in EncryptString. First you have:
ms.Write(aes.IV, 0, aes.IV.Length); // aes.IV.Length should be 16
which is the start of encryptedData. You then copy the IV and encryptedData (which already includes the IV) into a new byte array:
// this will hold the IV prepended to the encrypted data
byte[] output = new byte[aes.IV.Length + encryptedData.Length];
Array.Copy(aes.IV, output, aes.IV.Length); // save the iv
Array.Copy(encryptedData, 0, output, aes.IV.Length, encryptedData.Length); // save the data
This doubling of the IV is what is causing the extra bytes.
You don’t need to do the second copying. Just convert encryptedData to base 64 directly and return that:
return System.Convert.ToBase64String(encryptedData);

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.

Rijndael initialization vector with 16 bytes

I have to credit card number to be encrypted and stored in database as a string. I do the following
RijndaelManaged rijMan = new RijndaelManaged();
byte[] encrypt = Encrypt(txtCredit.Text, rijMan.Key, rijMan.IV);
string card = Convert.ToBase64String(encrypt);
string key = Convert.ToBase64String(rijMan.Key);
string iv = Convert.ToBase64String(rijMan.IV);
and this is encrypt function:
public static byte[] Encrypt(string message, byte[] key, byte[] iv)
{
byte[] encrypted;
using (RijndaelManaged rijMan = new RijndaelManaged())
{
rijMan.Key = key;
rijMan.IV = iv;
ICryptoTransform encrypt = rijMan.CreateEncryptor(rijMan.Key, rijMan.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(message);
}
encrypted = ms.ToArray();
}
}
}
return encrypted;
}
And i store them all in database. This seems to be fine. But when i retrieve back the string from database and use System.Text.Encoding.UTF8.GetBytes(string) to convert to byte, it throws an exception
Specified initialization vector (IV) does not match the block size for this algorithm.
Is it possible to generate the IV such that it is 16 bytes ? Thanks in advance for the reply
In the top of your post you are using BASE64 algorithm to convert IV bytes to string
string ivString = Convert.ToBase64String(rijMan.IV);
and then in the bottom of your post you are using UTF8 encoding to read string into byte array
byte[] iv = System.Text.Encoding.UTF8.GetBytes(ivString);
but you should use BASE64 algorithm again:
byte[] iv = Convert.FromBase64String(ivString);
BTW: I hope you are not storing AES key (rijMan.Key) in the same database as your encrypted credit card number because that would make whole encryption completely useless.

Categories

Resources