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.
Related
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;
}
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?
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 :)
I have a problem with AES Rijndael Managed encryption in C#
I have a list of around 110,000 image filenames that I want to encrypt individually. I have written an algorithm that encrypts them all together and then a method that decrypts a single image.
The encryption method is as follows:
var saltBytes = Encoding.UTF8.GetBytes(salt);
// The key size for encrypting the images
int KEY_SIZE = 256;
// The initialization vector for encrypting the images
byte[] _initialisationVectorBytes = Encoding.ASCII.GetBytes("EAzjVfNrCzOoE7AI");
//Derives the key from the phrase and the salt
using(var password = new Rfc2898DeriveBytes(password, saltBytes))
{
//use the AES Rijndael algorithm
using(var symmetricKey = new RijndaelManaged())
{
//set the mode
symmetricKey.Mode = CipherMode.CBC;
//create an encryption object
using(var encryptor = symmetricKey.CreateEncryptor(password.GetBytes(KEY_SIZE / 8), _initialisationVectorBytes))
{
//loop through the images
Parallel.ForEach(_sourceImages, image =>
{
//set the plain text bytes and the salt bytes
var plainTextBytes = Encoding.UTF8.GetBytes(image.ImageID.ToString());
var cipherText = string.Empty;
if(_encryptImageFilenames)
{
//create a memory stream
using(var cryptoMemoryStream = new MemoryStream())
{
//create a cryptographic stream
using(var cryptoStream = new CryptoStream(cryptoMemoryStream, encryptor, CryptoStreamMode.Write))
{
//encrypt the image id
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
cipherText = Convert.ToBase64String(cryptoMemoryStream.ToArray());
}
}
}
else
cipherText = image.ImageID.ToString();
}
}
}
}
This works as far as I can tell really well. However when I run the following decryption method, The vast majority of images decrypt correctly however, some images decrypt incorrectly with weird characters, and some give a "Padding is invalid and cannot be removed" error.
// The size of the key
const int KeySize = 256;
// Use the Rijndael Managed algorithm
RijndaelManaged SymmetricKey = new RijndaelManaged();
// The initialisation vector
byte[] initialisationVectorBytes = Encoding.ASCII.GetBytes("EAzjVfNrCzOoE7AI");
try
{
//get the cipher text bytes and the salt bytes
var cipherTextBytes = Convert.FromBase64String(cipherText);
//Derives the key from the phrase and the salt
using(var password = new Rfc2898DeriveBytes(passPhrase, Encoding.UTF8.GetBytes(salt)))
{
//set the mode
SymmetricKey.Mode = CipherMode.CBC;
//create an decryption object
using(var decryptor = SymmetricKey.CreateDecryptor(password.GetBytes(KeySize / 8), initialisationVectorBytes))
{
//create a memory stream
using(var memoryStream = new MemoryStream(cipherTextBytes))
{
//create a crypto stream
using(var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
//decrypt the string
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
catch(Exception ex)
{
return string.Empty;
}
So with 110000 images I get a succesful decrypt for all but around 20-30 images, with the failures being one of the two errors earlier.
I cannot for the life of me see why as surely if there was an error in the encrypt method it would affect all images that have been encrypted?
some examples:
Plaintext = 128784
Ciphertext = J2W/5Y1/nvnrpvaWIZhl6g==
Decrypted = 128784
Plaintext = 122875
Ciphertext = N+heUx57427Lk8/Ew10rsA==
Decrypted = �\u0001\u0017275
Plaintext = 121693
Ciphertext = Zf70jcYCbHQqhd23NqD6yA==
Decrypted = 121693
Plaintext = 133456
Ciphertext = wzBgoDaTnGBEyQokI+l6Uw==
Decrypted = "Padding is invalid and cannot be removed"
The password and salt used in these examples are:
Password = "!XbLg`p/0.9nyF?;jGf#nA;e19'TtVk?Ik.l*2=zy.9MYH:7Jmj[|*0N!"
Salt = "alkfdslkaj:H6D£rWe!N,K;?sdoidnalks34334234&(*£';"
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).