I have a PHP program that encrypts a PDF file into .xxx file this output is being read by a C# program that decrypts this .xxx file back into PDF file.
My problem is that when I open the file decrypted by C# , the PDF reader tells me that the file is corrupted .. when I encrypt plain text in PHP and decrypt on C# I got the file I encrypted .. so the problem is appearing only in PDF files or in other words it appears in BINARY files
any suggestions ?!
Notes:
In PHP I use mcrypt extension Rijndael algorithm CBC PKCS7 padding (padding is done manually)
In C# I use RijndaelManaged class to encrypt and decrypt data
Edit:
Here is encryption method that I use in PHP:
function encrypt($key, $iv, $text) {
ini_set ( 'memory_limit', '-1' );
$mcrypt_cipher = MCRYPT_RIJNDAEL_256;
$mcrypt_mode = MCRYPT_MODE_CBC;
$text=addpadding($text,mcrypt_get_block_size($mcrypt_cipher,'cbc'));
$encrypted = rtrim ( mcrypt_encrypt ( $mcrypt_cipher, $key, $text, $mcrypt_mode, $iv ), "\0" );
$encrypted = base64_encode ( $encrypted );
return $encrypted;
}
And here is the decryption method in C#:
public static string DecryptString(string message, string KeyString, string IVString)
{
byte[] Key = Encoding.UTF8.GetBytes(KeyString);
byte[] IV = Encoding.UTF8.GetBytes(IVString);
string decrypted = null;
RijndaelManaged rj = new RijndaelManaged();
rj.BlockSize = 256;
rj.Key = Key;
rj.IV = IV;
rj.Mode = CipherMode.CBC;
rj.Padding = PaddingMode.PKCS7;
try
{
MemoryStream ms = new MemoryStream();
//Encoding enc = new UTF8Encoding();
byte[] messageBytes = Convert.FromBase64String(message);
using (CryptoStream cs = new CryptoStream(ms, rj.CreateDecryptor(Key, IV), CryptoStreamMode.Write))
{
//byte[] messageBytes = enc.GetBytes(message);
cs.Write(messageBytes, 0, messageBytes.Length);
cs.Close();
}
byte[] encoded = ms.ToArray();
decrypted = Encoding.UTF8.GetString(encoded);
ms.Close();
}
catch (Exception e)
{
MessageBox.Show("An error occurred:"+ e.Message);
}
finally
{
rj.Clear();
}
return decrypted;
}
and here is how I call the decrypt in C# and how I write output:
string Key = cryptography.MD5("X-Ware" + cryptography.MD5("123"));
string IV = cryptography.MD5("XWare");
string decrypted = cryptography.DecryptString(contents, Key, IV);
string outputFilename = cryptography.MD5(OFD.FileName) + ".tmp";
StreamWriter sw = new StreamWriter("C:\\Windows\\Temp\\" + outputFilename, false, Encoding.UTF8);
BinaryWriter bw = new BinaryWriter(sw.BaseStream, Encoding.UTF8);
//sw.Write(decrypted);
bw.Write(decrypted);
sw.Close();
bw.Close();
I think the problem is that you treat the binary PDF data as text on both the PHP and the C# side.
decrypted = Encoding.UTF8.GetString(encoded);
makes no sense if encoded represents binary data. You should probably skip this step and define your DecryptString() as returning byte[]. And then rename it too.
If you do want it as a string you might have better luck with ASCII or ANSI encoding:
decrypted = Encoding.ASCII.GetString(encoded);
but the error may already be happening on the PHP side, I can't tell.
Additional, I just noted:
StreamWriter sw = new StreamWriter("C:\\Windows\\Temp\\" + outputFilename,
false, Encoding.UTF8);
BinaryWriter bw = new BinaryWriter(sw.BaseStream, Encoding.UTF8);
This is a very over-complicated way to create a BinaryWriter. The Encoding will not be used. And
bw.Write(decrypted);
This will write the string with a length-prefix, that certainly will make your PDF invalid.
When you keep the return of Decrypt as string, use
File.WriteAllText("C:\\Windows\\Temp\\" + outputFilename, decrypted);
And when you return it as byte[] (recommended), use
File.WriteAllBytes("C:\\Windows\\Temp\\" + outputFilename, decrypted);
Related
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.
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);
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.
So, I am having an issue with decrypting the decoded base64 aes string. Is this possible? I wrote a small console program to work this out but no luck. Here is my example:
As depicted, I have successfully converted the base64 back the aes encrypted string, but when I try to decrypt it I get more junk. If a code snippet is need let me. Thank you all for your help :)
UPDATE: Code snippet for decrypting method
static void Main(string[] args)
{
string plainText;
string decrypted;
string decryptedFromB64EncodedDecoded;
string fromBase64ToEncryptedText;
string encryptedText;
string encryptedTextBase64;
byte[] encryptedBytes;
byte[] encryptedBytes2;
byte[] encryptedBytesBase64;
RijndaelManaged crypto = new RijndaelManaged();
UTF8Encoding UTF = new UTF8Encoding();
Console.WriteLine("Please put in the text to be encrypted.");
plainText = Console.ReadLine();
try
{
encryptedBytes = encrypt(plainText, crypto.Key, crypto.IV);
encryptedText = Encoding.ASCII.GetString(encryptedBytes);
//encryptedBytes2 = Encoding.ASCII.GetBytes(encryptedText);
encryptedTextBase64 = toBase64String(encryptedText);
encryptedBytesBase64 = fromBase64String(encryptedTextBase64);
fromBase64ToEncryptedText = Encoding.ASCII.GetString(encryptedBytesBase64);
encryptedBytes2 = Encoding.ASCII.GetBytes(fromBase64ToEncryptedText);
decrypted = decrypt(encryptedBytes, crypto.Key, crypto.IV);
decryptedFromB64EncodedDecoded = decrypt(encryptedBytes2, crypto.Key, crypto.IV);
Console.WriteLine("Start: {0}", plainText);
Console.WriteLine("Encrypted: {0}", encryptedText);
Console.WriteLine("Encrypted Base64: {0}", encryptedTextBase64);
Console.WriteLine("From Base64 To AES Encypted Text: {0}", fromBase64ToEncryptedText);
Console.WriteLine("Decrypted: {0}", decrypted);
Console.WriteLine("Decrypted From Encode and then Decode Base64 Text: {0}", decryptedFromB64EncodedDecoded);
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.ReadLine();
}
public static string decrypt (byte[] textToDecrypt, byte[] key, byte[] IV)
{
RijndaelManaged crypto = new RijndaelManaged();
MemoryStream stream = new MemoryStream(textToDecrypt) ;
ICryptoTransform decryptor = null;
CryptoStream cryptoStream = null;
StreamReader readStream = null;
string text = string.Empty;
try
{
crypto.Key = key;
crypto.IV = IV;
crypto.Padding = PaddingMode.None;
decryptor = crypto.CreateDecryptor(crypto.Key, crypto.IV);
cryptoStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Read);
//cryptoStream.Read(textToDecrypt, 0, textToDecrypt.Length);
readStream = new StreamReader(cryptoStream);
text = readStream.ReadToEnd();
cryptoStream.Close();
byte[] decodedValue = stream.ToArray();
return text;
}
catch (Exception)
{
throw;
}
finally
{
if (crypto != null)
{
crypto.Clear();
}
stream.Flush();
stream.Close();
}
}
public static byte[] encrypt(string text, byte[] key, byte[] IV)
{
RijndaelManaged crypto = null;
MemoryStream stream = null;
//ICryptoTransform is used to perform the actual decryption vs encryption, hash function are a version crypto transforms
ICryptoTransform encryptor = null;
//CryptoStream allows for encrption in memory
CryptoStream cryptoStream = null;
UTF8Encoding byteTransform = new UTF8Encoding();
byte[] bytes = byteTransform.GetBytes(text);
try
{
crypto = new RijndaelManaged();
crypto.Key = key;
crypto.IV = IV;
stream = new MemoryStream();
encryptor = crypto.CreateEncryptor(crypto.Key, crypto.IV);
cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(bytes, 0, bytes.Length);
}
catch (Exception)
{
throw;
}
finally
{
if (crypto != null)
{
crypto.Clear();
}
cryptoStream.Close();
}
return stream.ToArray();
}
public static string toBase64String(string value)
{
UTF8Encoding UTF = new UTF8Encoding();
byte[] myarray = UTF.GetBytes(value);
return Convert.ToBase64String(myarray);
}
public static byte[] fromBase64String(string mystring)
{
//UTF8Encoding UTF = new UTF8Encoding();
//byte[] myarray = UTF.GetBytes(value);
return Convert.FromBase64String(mystring);
}
I don't know how you're decrypting but before you decrypt, you should convert the base 64 string to a byte array before sending it into the decryption.
byte[] encryptedStringAsBytes = Convert.FromBase64String(base64EncodedEncryptedValue);
Then with the byte array you can pass to the CryptoStream via a MemoryStream.
UPDATE
I believe the issue is how you're setting up your streams
using (RijndaelManaged rijndaelManaged = new RijndaelManaged())
{
rijndaelManaged.Padding = paddingMode;
rijndaelManaged.Key = key;
rijndaelManaged.IV = initVector;
MemoryStream memoryStream = null;
try
{
memoryStream = new MemoryStream(valueToDecrypt);
using (ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader(cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
finally
{
if (memoryStream != null)
memoryStream.Dispose();
}
}
UPDATE 2
This is how you should basically perform the steps.
To encrypt
Encode your plain text string using the Encoding.GetBytes(stringToEncrypt)
pass the byte[] into the crypto API (via memory stream, etc.)
get the bytes from the encrypted stream and encode the results as Base64
To Decrypt (do the reverse)
Convert the base64 encoded string to bytes using Convert.FromBase64String(base64EncodedEncryptedValue)
pass that byte array into your decryption function above
Try:
encryptedBytes2 = Encoding.ASCII.GetBytes(encryptedText);
Based on your comment. The bytes are just that bytes, so in order to decrypt the ciphertext you need to undo any encoding or series of encodings you have done.
If you really want to go from Encrypted Bytes -> Base64String -> ASCII string -> then decrypt that ASCII string? you would need to base64 decode the ascii string then convert that string to bytes using
Encoding.ASCII.GetBytes(yourdecodedstring);
Note that base 64 decoding is not the same as using Convert.FromBase84String.
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).