Decryption with CryptoStream using aes fails - c#

I'm writing two applications one encrypts files the other has to read. While the encryption and decryption in the first program works the second is not able to decrypt the file.
This is the code that encrypts the data:
using (Aes aes = getAes())
{
ICryptoTransform aesEncrypt = aes.CreateEncryptor();
doCrypt(filePath, aesEncrypt);
}
with doCrypt:
private void doCrypt(string filePath, ICryptoTransform aesCrypto)
{
doCrypt(filePath, filePath, aesCrypto);
}
private void doCrypt(string sourceFilePath, string targetFilePath, ICryptoTransform aesCrypto)
{
byte[] input = File.ReadAllBytes(sourceFilePath);
FileStream cryptoFS = new FileStream(targetFilePath, FileMode.Create);
CryptoStream cryptoStream = new CryptoStream(cryptoFS, aesCrypto, CryptoStreamMode.Write);
cryptoStream.Write(input, 0, input.Length);
cryptoStream.Close();
cryptoFS.Close();
}
and getAes:
public Aes getAes()
{
Aes aes = AesCryptoServiceProvider.Create();
MD5 keyHasher = MD5.Create();
aes.Mode = CipherMode.ECB;
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Padding = PaddingMode.Zeros;
aes.Key = keyHasher.ComputeHash(System.Text.Encoding.ASCII.GetBytes(pswdTB.Text));
return aes;
}
Note: IV is not set, because aes runs in ECB-Mode, which does not use an IV
And this is the code to decrypt in the second application:
Aes aes = getAes();
//MemoryStream ms = new MemoryStream();
//CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write);
//cs.Write(data, 0, data.Length);
//cs.Close();
//result = ms.ToArray();
//ms.Close();
FileStream cryptoFS = new FileStream("test.txt", FileMode.Create);
CryptoStream cryptoStream = new CryptoStream(cryptoFS, aes.CreateDecryptor(), CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.Close();
cryptoFS.Close();
aes.Dispose();
I need the result to be in the byte[] result, but for debugging purposes I replaced it with a file output.
Edit While I do get output, it's not decrypted properly.
This is a short peace of the resulting test.txt: "ꕾ㩈ႂ⤙͊꼥聓綨炌磌腼ꀽ鞍壃"
The file was an XML file before en- and decrypton.
The getAes here is a copy, the only difference is, that the key is supplied by another method. But I checked the keys used are equal.

In .NET the CryptoStream has two "modes".
To encrypt you build it like:
CryptoStream cryptoStream = new CryptoStream(outputEncryptedStream, aesCrypto, CryptoStreamMode.Write);
to decrypt you build it like:
CryptoStream cryptoStream = new CryptoStream(inputEncryptedStream, aesCrypto, CryptoStreamMode.Read);
In both cases you must pass the encrypted/would be encrypted stream as the first parameter.
To encrypt something you can:
input.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
To decrypt something you can
cryptoStream.CopyTo(output);
(you don't need/can't use FlushFinalBlock to decrypt)
where input and output are the non-encrypted streams.

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.

C# stream reader ReadToEnd() missing last character

I am trying to decrypt a string in C# using AES:
public static string AesDecrypt(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decryptor to perform the stream transform
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption
using MemoryStream msDecrypt = new MemoryStream(cipherText);
using CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
using StreamReader srDecrypt = new StreamReader(csDecrypt);
// Read the decrypted bytes from the decrypting stream and place them in a string
plaintext = srDecrypt.ReadToEnd();
return plaintext;
}
The encoded data is JSON, but when I decrypt it, I get all the right data except that the closing } of the JSON content is missing.
I think that the AES itself is not my problem here. I have doubts in the
plaintext = srDecrypt.ReadToEnd();
since only the last character is missing.
I don't know if I am supposed to flush any of the streams explicitly, but in any case it's a very curious problem.
Here's the full code for the encryption:
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
// Write all data to the stream
swEncrypt.Write(plainText);
swEncrypt.Flush();
return Convert.ToBase64String(msEncrypt.ToArray());
}
And this is how I call the decryption method:
public static AuthenticationData ParseAuthenticationToken(string token)
{
byte[] tokenBytes = Convert.FromBase64String(token);
string json = AesEncryption.AesDecrypt(tokenBytes, aes.Key, aes.IV);
return JsonConvert.DeserializeObject<AuthenticationData>(json);
}
The problem is in your encryption code. Although you're calling seEncrypt.Flush(), you're not calling csEncrypt.FlushFinalBlock(). That automatically happens when the stream is disposed, but you're not doing that until after you've called msEncrypt.ToArray(). I would rewrite that code as:
MemoryStream msEncrypt = new MemoryStream();
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
swEncrypt.Write(plainText);
// swEncrypt is disposed here, flushing it. Then csEncrypt is disposed,
// flushing the final block.
}
return msEncrypt.ToArray();

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.

Encrypting with AES

Why can I encrypt only 16 characters of text?
Works:
string plainText = "1234567890123456";
Doesn't work:
string plainText = "12345678901234561";
Doesn't work:
string plainText = "123456789012345";
Code:
string plainText = "1234567890123456";
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes("1234567890123456");
byte[] initVectorBytes = System.Text.Encoding.UTF8.GetBytes("1234567890123456");
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.Zeros;
ICryptoTransform encryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string cipherText = Convert.ToBase64String(cipherTextBytes);
Console.ReadLine();
Not sure I understand the question, but looking at what I assume the intent is of the code the following
symmetricKey.CreateDecryptor
Should probably be
symmetricKey.CreateEncryptor
Probably because AES is a block cipher with 128 bits per block.. maybe you just need to add a padding such that length % 128 == 0.
(I'm not a C# developer but it can happen that an implementation doesn't care about adding padding by itself)
Just a hint: try if it works with 256 bits

"Padding is invalid and cannot be removed" using AesManaged

I'm trying to get simple encryption/decryption working with AesManaged, but I keep getting an exception when trying to close the decryption stream. The string here gets encrypted and decrypted correctly, and then I get the CryptographicException "Padding was invalid and cannot be removed" after Console.WriteLine prints the correct string.
Any ideas?
MemoryStream ms = new MemoryStream();
byte[] rawPlaintext = Encoding.Unicode.GetBytes("This is annoying!");
using (Aes aes = new AesManaged())
{
aes.Padding = PaddingMode.PKCS7;
aes.Key = new byte[128/8];
aes.IV = new byte[128/8];
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(),
CryptoStreamMode.Write))
{
cs.Write(rawPlaintext, 0, rawPlaintext.Length);
cs.FlushFinalBlock();
}
ms = new MemoryStream(ms.GetBuffer());
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(),
CryptoStreamMode.Read))
{
byte[] rawData = new byte[rawPlaintext.Length];
int len = cs.Read(rawData, 0, rawPlaintext.Length);
string s = Encoding.Unicode.GetString(rawData);
Console.WriteLine(s);
}
}
The trick is to use MemoryStream.ToArray().
I also changed your code so that it uses the CryptoStream to Write, in both encrypting and decrypting. And you don't need to call CryptoStream.FlushFinalBlock() explicitly, because you have it in a using() statement, and that flush will happen on Dispose(). The following works for me.
byte[] rawPlaintext = System.Text.Encoding.Unicode.GetBytes("This is all clear now!");
using (Aes aes = new AesManaged())
{
aes.Padding = PaddingMode.PKCS7;
aes.KeySize = 128; // in bits
aes.Key = new byte[128/8]; // 16 bytes for 128 bit encryption
aes.IV = new byte[128/8]; // AES needs a 16-byte IV
// Should set Key and IV here. Good approach: derive them from
// a password via Cryptography.Rfc2898DeriveBytes
byte[] cipherText= null;
byte[] plainText= null;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(rawPlaintext, 0, rawPlaintext.Length);
}
cipherText= ms.ToArray();
}
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
}
plainText = ms.ToArray();
}
string s = System.Text.Encoding.Unicode.GetString(plainText);
Console.WriteLine(s);
}
Also, I guess you know you will want to explicitly set the Mode of the AesManaged instance, and use System.Security.Cryptography.Rfc2898DeriveBytes to derive the Key and IV from a password and salt.
see also:
- AesManaged
This exception can be caused by a mismatch of any one of a number of encryption parameters.
I used the Security.Cryptography.Debug interface to trace all parameters used in the encrypt/decrypt methods.
Finally I found out that my problem was that I set the KeySize property after setting the Key causing the class to regenerate a random key and not using the key that I was initially set up.
For whats its worth, I'll document what I faced. I was trying to read the encryptor memory stream before the CryptoStream was closed. I was naive and I wasted a day debugging it.
public static byte[] Encrypt(byte[] buffer, byte[] sessionKey, out byte[] iv)
{
byte[] encrypted;
iv = null;
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
{
aesAlg.Key = sessionKey;
iv = aesAlg.IV;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(sessionKey, iv);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(buffer, 0, buffer.Length);
//This was not closing the cryptostream and only worked if I called FlushFinalBlock()
//encrypted = msEncrypt.ToArray();
}
encrypted = msEncrypt.ToArray();
return encrypted;
}
}
}
Moving the encryptor memory stream read after the cypto stream was closed solved the problem. As Cheeso mentioned. You don't need to call the FlushFinalBlock() if you're using the using block.
byte[] rawData = new
byte[rawPlaintext.Length];
You need to read the length of the buffer, that probably includes the necessary padding (IIRC, been a few years).
Nobody answered, that actually MemoryStream.GetBuffer returns the allocated buffer, not the real data in this buffer. In this case it returns 256-byte buffer, while it contains only 32 bytes of encrypted data.
As others have mentioned, this error can occur if the key/iv is not correctly initialized for decryption. In my case I need to copy key and iv from some larger buffer. Here's what I did wrong:
Does not work: (Padding is invalid and cannot be removed)
aes.Key = new byte[keySize];
Buffer.BlockCopy(someBuffer, keyOffset, aes.Key, 0, keySize);
aes.IV = new byte[ivSize];
Buffer.BlockCopy(someBuffer, ivOffset, aes.IV, 0, ivSize);
Works:
var key = new byte[keySize];
Buffer.BlockCopy(someBuffer, keyOffset, key, 0, keySize);
aes.Key = key;
var iv = new byte[ivSize];
Buffer.BlockCopy(someBuffer, ivOffset, iv, 0, ivSize);
aes.IV = iv;
The OP did not make this mistake, but this might be helpful for others seeing the same error.

Categories

Resources