Cannot decrypt data with C# that was encrypted using PHP (Rijdael-128) - c#

I decrypt data using PHP with this code:
$content="1234";
$cp = mcrypt_module_open('rijndael-128', '', 'cbc', '');
$iv = mcrypt_create_iv(16, MCRYPT_RAND);
$key = pack("H*",md5('a'));
mcrypt_generic_init($cp, $key, $iv);
$encrypted = mcrypt_generic($cp, $content);
echo base64_encode($key)."\n";
echo base64_encode($iv)."\n";
echo base64_encode($encrypted)."\n";
mcrypt_generic_deinit($cp);
mcrypt_module_close($cp);
$iv and $encrypted is then saved to file and read in the C# sample app:
var iv=...;
var encrypted=...;
var md5 = new MD5CryptoServiceProvider();
var key = md5.ComputeHash(Encoding.Default.GetBytes("a"));
md5.Clear();
Console.WriteLine(Convert.ToBase64String(key));
Console.WriteLine(Convert.ToBase64String(iv));
Console.WriteLine(Convert.ToBase64String(encrypted));
The output here is exactly the same as the output from PHP, so I can assure there is no encoding error inbetween.
var rd = new RijndaelManaged {
Key = key,
IV = iv,
Mode = CipherMode.CBC,
KeySize = 128,
Padding = PaddingMode.Zeros
};
var buffer = new byte[encrypted.Length];
using(var ms = new MemoryStream(buffer)) {
using(var cs = new CryptoStream(ms, rd.CreateDecryptor(), CryptoStreamMode.Write)) {
cs.Write(encrypted, 0, encrypted.Length);
ms.Read(buffer, 0, buffer.Length);
Console.WriteLine(Encoding.Default.GetString(buffer));
}
}
rd.Clear();
The result of the decryption varies on every program start, even with exactly the same input data:
First run:
DMF1ucDxtqgxw5niaXcmYQ== <-Key
GoCeRkrL/EMKNH/BYeLsqQ== <-IV
UBE3DkgbJgj1K/TISugLxA== <-Encrypted
OlOB99yiCYRDoLx+0xxZxQ== <-"Decrypted"
Second run:
DMF1ucDxtqgxw5niaXcmYQ== <-Key
GoCeRkrL/EMKNH/BYeLsqQ== <-IV
UBE3DkgbJgj1K/TISugLxA== <-Encrypted
w5fcY5Fbb9KRgoHfhqAztA== <-"Decrypted"
Key, IV, Encrypted data are identical, but still the decrypted date varies and is always wrong. buffer should contain "1234" or "1234" plus 12 trailing zeros.
I don't see why the results vary and what is not working, but I have been staring at this darn piece of code for several hours now, and probably miss the obvious error...
Reversing the CryptoStream like this creates identically wrong results:
using(var ms = new MemoryStream(encrypted)) {
using(var cs = new CryptoStream(ms, rd.CreateDecryptor(), CryptoStreamMode.Read)) {
cs.Read(buffer, 0, buffer.Length);
Console.WriteLine(Convert.ToBase64String(buffer));
}
}
Help?
Thanks!
Alexander

Well, modifying an old sample of my sins of the past I ended up with this:
static string Decrypt() {
byte[] keyBytes = Convert.FromBase64String("DMF1ucDxtqgxw5niaXcmYQ==");
byte[] iv = Convert.FromBase64String("GoCeRkrL/EMKNH/BYeLsqQ==");
byte[] cipherTextBytes = Convert.FromBase64String("UBE3DkgbJgj1K/TISugLxA==");
var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC, IV = iv, KeySize = 128, Key = keyBytes, Padding = PaddingMode.Zeros};
using (var decryptor = symmetricKey.CreateDecryptor())
using (var ms = new MemoryStream(cipherTextBytes))
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) {
var plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cs.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
which gave "1234" with trailing \0 characters.. Did you just forget to convert the byte[] to a string again? What other difference am I missing?

Related

Trouble Encrypting/Decrypting string in C# with random IV

I am rewriting some code to have a random IV that is prepended to my encrypted string. The encryption I think is correct (can't decrypt so I'm not 100% sure), but the problem is when I run the decrypt function, I get an exception
System.Security.Cryptography.CryptographicException: 'The input data is not a complete block.'
Below is the source code. Does anyone know where I'm going wrong? I think the error is in the binaryWritr.Write call, but after several iterations, I'm not making progress. Any help would be greatly appreciated!
public static string EncryptString(string inputText)
{
using(var aes = new AesCryptoServiceProvider() {
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
}) {
var input = Encoding.UTF8.GetBytes(inputText);
aes.GenerateIV();
var iv = aes.IV;
using(var encrypter = aes.CreateEncryptor(aes.Key, iv))
using(var cipherStream = new MemoryStream()) {
using(var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using(var tBinaryWriter = new BinaryWriter(tCryptoStream)) {
//Prepend IV to data
cipherStream.Write(iv, 0, iv.Length);
tBinaryWriter.Write(input);
tCryptoStream.FlushFinalBlock();
}
return Convert.ToBase64String(cipherStream.ToArray());
}
}
}
public static string DecryptString(string inputText)
{
var aes = new AesCryptoServiceProvider() {
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
//get first 16 bytes of IV and use it to decrypt
var iv = new byte[16];
Array.Copy(Encoding.ASCII.GetBytes(inputText), 0, iv, 0, 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)) {
//Decrypt Cipher Text from Message
binaryWriter.Write(
Encoding.ASCII.GetBytes(inputText),
iv.Length,
inputText.Length - iv.Length
);
}
return Encoding.Default.GetString(ms.ToArray());
}
}

AES encryption error: The input data is not a complete block?

Here's the encryption method:
public static byte[] Encrypt(byte[] plaintext, byte[] key)
{
using (var aes = Aes.Create())
{
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
var iv = new byte[16];
for (int i = 0; i < iv.Length; i++)
iv[i] = 0;
aes.IV = iv;
var encryptor = aes.CreateEncryptor(key, aes.IV);
using(var target = new MemoryStream())
using (var cs = new CryptoStream(target, encryptor, CryptoStreamMode.Write))
{
using (var source = new StreamWriter(cs))
source.Write(plaintext);
return target.ToArray();
}
}
}
And how I'm calling it:
var key = new byte[16] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var plaintext = new byte[16] { 128, 0, 112, 0, 96, 0, 80, 0, 64, 0, 48, 0, 32, 0, 16, 0 };
But it keeps throwing an exception at source.Write(plaintext) that says it's not a complete block? I'm using a 16 byte/ 128 bit array with the block size set to 128. I don't understand what's wrong?
Also, just to head off any suggestions that ECB is bad etc, this is not for production, I'm just playing around.
StreamWriter writes UTF8 text characters to a stream.
You're writing plaintext.ToString() as text for the ciphertext.
This returns "System.Byte[]", which does not translate into 16 bytes of UTF8.
I believe the problem to be padding mode. Unless your text to be encrypted is for sure divisible by BlockSize (in bits, or BlockSize / 8 in bytes), you should specify a PaddingMode other than None.
see the post here for example code
I changed the function to this:
public static byte[] Encrypt(byte[] plaintext, byte[] key)
{
using (var aes = Aes.Create())
{
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
var encryptor = aes.CreateEncryptor(key, new byte[16]);
using(var target = new MemoryStream())
using (var cs = new CryptoStream(target, encryptor, CryptoStreamMode.Write))
{
cs.Write(plaintext, 0, plaintext.Length);
return target.ToArray();
}
}
}
From what I have experienced and fixed this issue, I missed to encrypt the field that it is complaining about, I was only decrypting it. It was saving a plain clear text and trying to decrypt it. So just make sure that the field that is complaining about is encrypted first. Thanks
This is valid for PowerShell:
This could happen if you encrypted the password without using -key option. To fix it encrypt it using -key option:
ConvertFrom-SecureString -key $key

PHP mcrypt_encrypt to .NET

I have almost lost my hair, mind and everything else! I have been trying to convert this PHP function to C#:
function encrypt_decrypt($action, $string) {
$output = false;
$key = 'My strong secret key';
// initialization vector
$iv = md5(md5($key));
$output = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, $iv);
$output = bin2hex($output);
return $output;
}
I have been working with Rijandel Class:
function encrypt_decrypt(string password) {
UTF8Encoding encoding = new UTF8Encoding();
// For consistency with PHP function, MD5Encrypt applies MD5 encryption and does a bin2hex
byte[] Key = Encoding.ASCII.GetBytes(MD5Encrypt(password).ToLower());
byte[] IV = Encoding.ASCII.GetBytes(MD5Encrypt(MD5Encrypt(password).ToLower()).ToLower());
RijndaelManaged rj = new RijndaelManaged();
rj.BlockSize = 256;
rj.KeySize = 256;
rj.Key = Key;
rj.IV = IV;
rj.Mode = CipherMode.CBC;
MemoryStream ms = new MemoryStream();
using (CryptoStream cs = new CryptoStream(ms, rj.CreateEncryptor(Key, IV), CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(message);
sw.Close();
}
cs.Close();
}
byte[] encoded = ms.ToArray();
string output = "";
foreach (var ele in encoded)
{
output += ele.ToString("X2");
}
return output;
}
I have been validating the output of the PHP code with that from the C# code and they do not match. (http://writecodeonline.com/php/). Any feedback would be appreciated.
There are multiple issues to be kept in mind while doing this like converting binary, checking encoding and padding issues. Since we cannot see your complete code we are helpless in this case. Check this tutorial for further info: http://blog.djekldevelopments.co.uk/?p=334
Try this instead:
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
myRijndael.Key = Encoding.UTF8.GetBytes(password);
string strIv16 = "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0";
myRijndael.IV = Encoding.UTF8.GetBytes(strIv16);
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes(message, myRijndael.Key, myRijndael.IV);
string output = Convert.ToBase64String(encrypted);
}

Decrypt the data using the decrypted key

I am trying to decrypt the data using private key. I was able to decrypt the key using RSA and private key. Now I would like to decrypt the data using the decrypted key. The data was encrypted the values using AES and random session secret using PHP.
Could you please let me know if there are any examples?
Here is the code I have so far.
static void Main(string[] args)
{
AsymmetricCipherKeyPair keyPair;
string protectedSecret = "U6XksFkhWV4.......eo3fRg==";
var decodedSecret = Convert.FromBase64String(protectedSecret);
string iv = "KLnP....wA==";
var decodedIV = Convert.FromBase64String(iv);
using (var reader = File.OpenText(#"c:\\private.key"))
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
var decryptPKIEngine = new Pkcs1Encoding(new RsaEngine());
decryptPKIEngine.Init(false, keyPair.Private);
var secret = Encoding.UTF8.GetString(decryptPKIEngine.ProcessBlock(decodedSecret, 0, decodedSecret.Length));
var protectedData = Convert.FromBase64String("f8..Po=");
}
Create a RijndaelManaged instance and set its Key and IV to your byte arrays.
Then, create a CryptoStream from CreateDecryptor() wrapping a MemoryStream with your ciphertext byte array.
Finally, read the plaintext from the CryptoStream. (if it's actual text, you may want to use a StreamReader)
Try this replace put the necessary strings where needed
static string PHPDecrypt()
{
byte[] keyBytes = Convert.FromBase64String("U6XksFkhWV4.......eo3fRg=="); //put in your real values here and below for iv and cipherTextBytes
byte[] iv = Convert.FromBase64String("KLnP....wA=="");
byte[] cipherTextBytes = Convert.FromBase64String("Put the EncryptedText here");
var symmetricKey = new RijndaelManaged
{
Mode = CipherMode.CBC,
IV = iv, KeySize = 256,
Key = keyBytes,
Padding = PaddingMode.Zeros
};
using (var decryptor = symmetricKey.CreateDecryptor())
using (var ms = new MemoryStream(cipherTextBytes))
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) {
var plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cs.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}

Decryption of file missing ~10 characters from ending

I've written Encryption/Decryption methods using the RC2CryptoServiceProvider in C# and for some reason, I cannot get my decryptor to decrypt the final few bytes. The file seems to just cut off. My encryption method looks like:
public static byte[] EncryptString(byte[] input, string password)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
byte[] IV = new byte[8];
ICryptoTransform encryptor = RC2.CreateEncryptor(pbeKey, IV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(input, 0, input.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
While my decryption looks like:
public static byte[] DecryptString(byte[] input, string password, int originalSize)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
byte[] IV = new byte[8];
ICryptoTransform decryptor = RC2.CreateDecryptor(pbeKey, IV);
MemoryStream msDecrypt = new MemoryStream();
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write);
csDecrypt.Write(input, 0, originalSize);
// csDecrypt.FlushFinalBlock();
char[] decrypted = new char[input.Length];
decrypted = System.Text.Encoding.UTF8.GetChars(msDecrypt.ToArray());
return msDecrypt.ToArray();
}
The char[] decrypted is returning the whole file decrypted, except the file ends with </LudoData> and when decrypting, I only get up to the first < character.
I have been playing with the lengths of things and nothing is changing anything. In my specific case, input is of length 11296, and originalSize is of size 11290. However, decrypted ends up being of size 11280 when decrypting. What gives!
Is there a reason that you have the Flush() commented out? Have you tried fully closing your streams?
Sigh, I fought this battle about a month ago and had a very similar issue, except I was experiencing TOO much on the end. ToArray was my solution.
You're doing some weird stuff here I'm not exactly sure of. You're using cryptostreams when you don't have to, you're keeping track of the original length for some weird reason and you're using deprecated classes. Your issue is probably a combination of padding, incorrect assumptions (evidenced by originalLength) and incorrect handling of streams (which can be tricky). Try this instead:
Encrypt:
var rij = RijndaelManaged.Create();
rij.Mode = CipherMode.CBC;
rij.BlockSize = 256;
rij.KeySize = 256;
rij.Padding = PaddingMode.ISO10126;
var pdb = new Rfc2898DeriveBytes(password,
Encoding.Default.GetBytes("lolwtfbbqsalt" + password));
var enc = rij.CreateEncryptor(pdb.GetBytes(rij.KeySize / 8),
pdb.GetBytes(rij.BlockSize / 8));
return enc.TransformFinalBlock(unencryptedBytes, 0, unencryptedBytes.Length);
Decrypt:
// throws a cryptographic exception if password is wrong
var rij = RijndaelManaged.Create();
rij.Mode = CipherMode.CBC;
rij.BlockSize = 256;
rij.KeySize = 256;
rij.Padding = PaddingMode.ISO10126;
var pdb = new Rfc2898DeriveBytes(password,
Encoding.Default.GetBytes("lolwtfbbqsalt" + password));
var dec = rij.CreateDecryptor(pdb.GetBytes(rij.KeySize / 8),
pdb.GetBytes(rij.BlockSize / 8));
return dec.TransformFinalBlock(encryptedBytes, 0,
encryptedBytes.Length);
Notice the only thing different within these two methods are CreateEncryptor/CreateDecryptor, so you can refactor out lots of duplication. Also notice I get in a byte array and get out a byte array without having to use any streams. Its also a bit more secure than RC2, and would be even more so if the salt were more random.

Categories