Compress and Encrypt vs. Decrypt and Decompress - c#

take a look at this to methods:
public void CompressAndEncrypt(Stream input, Stream output)
{
Aes aes = Aes.Create();
aes.Key = Key;
aes.IV = IV;
aes.Padding = PaddingMode.PKCS7;
aes.BlockSize = 128;
ICryptoTransform aesEncryptor = aes.CreateEncryptor();
using (CryptoStream cryptoStream = new(output, aesEncryptor, CryptoStreamMode.Write))
using (GZipStream compressedStream = new(cryptoStream, CompressionLevel.SmallestSize))
{
input.CopyTo(compressedStream);
cryptoStream.FlushFinalBlock();
}
}
and
public void DecryptAndDecompress(Stream input, Stream output)
{
Aes aes = Aes.Create();
aes.Key = Key;
aes.IV = IV;
aes.Padding = PaddingMode.PKCS7;
aes.BlockSize = 128;
ICryptoTransform aesDecryptor = aes.CreateDecryptor();
using (CryptoStream cryptoStream = new(input, aesDecryptor, CryptoStreamMode.Read))
using (GZipStream decompressedStream = new(cryptoStream, CompressionMode.Decompress))
{
decompressedStream.CopyTo(output);
}
}
The input and output Stream can be considered as FileStream. As you can see, the first method compresses and encrypt a Stream, while the second method decrypts and decompress a Stream.
The first method works fine, but if a take the result of the first method and pass it to the second, .NET says:
System.IO.InvalidDataException. "The archive entry was compressed using an unsupported compression method."
Any suggestion?

I suspect that the problem is with cryptoStream.FlushFinalBlock(); before disposing/flushing the compression stream. This slightly altered example works for me:
[Test]
public void EncryptDecrypt()
{
var source = new MemoryStream();
var data = new byte[] {42, 3, 5, 100, 255, 100, 255, 100, 255, 100, 255, 100, 255, 100, 255, 100, 255};
source.Write(data, 0, data.Length);
source.Position = 0;
var encrypted = new MemoryStream();
var (key, iv) = CompressAndEncrypt(source, encrypted);
var result = new MemoryStream();
encrypted.Position = 0;
DecryptAndDecompress(encrypted, result, key, iv);
result.Position = 0;
var resultData = new byte[data.Length];
result.Read(resultData, 0, resultData.Length);
CollectionAssert.AreEquivalent(data, resultData);
}
public (byte[] key, byte[] Iv) CompressAndEncrypt(Stream input, Stream output)
{
Aes aes = Aes.Create("AesManaged");
aes.GenerateIV();
aes.GenerateKey();
aes.Padding = PaddingMode.PKCS7;
aes.BlockSize = 128;
ICryptoTransform aesEncryptor = aes.CreateEncryptor();
using CryptoStream cryptoStream = new(output, aesEncryptor, CryptoStreamMode.Write, true);
using GZipStream compressedStream = new(cryptoStream, CompressionLevel.Optimal, true);
input.CopyTo(compressedStream);
return (aes.Key, aes.IV);
}
public void DecryptAndDecompress(Stream input, Stream output, byte[] key, byte[] iv)
{
Aes aes = Aes.Create("AesManaged");
aes.Key = key;
aes.IV = iv;
aes.Padding = PaddingMode.PKCS7;
aes.BlockSize = 128;
ICryptoTransform aesDecryptor = aes.CreateDecryptor();
using CryptoStream cryptoStream = new(input, aesDecryptor, CryptoStreamMode.Read, true);
using GZipStream decompressedStream = new(cryptoStream, CompressionMode.Decompress, true);
decompressedStream.CopyTo(output);
}

Related

RijndaelManaged "padding is invalid and cannot be removed"

I'm trying to create methods for very strong Rijndael 256 string encryption that I can use for passwords but I get an error saying Padding is invalid and cannot be removed. when I read the CryptoStream and get the decrypted string. Here are my encrypt and decrypt methods:
private string AES256EncryptString(string key, string plainText)
{
try
{
using (RijndaelManaged rijndael = new RijndaelManaged())
{
rijndael.KeySize = 256;
rijndael.BlockSize = 128;
rijndael.Key = Encoding.UTF8.GetBytes(key);
rijndael.GenerateIV();
rijndael.Mode = CipherMode.CBC;
rijndael.Padding = PaddingMode.PKCS7;
ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, rijndael.IV);
MemoryStream memoryStream = new MemoryStream();
memoryStream.Write(rijndael.IV, 0, rijndael.IV.Length);
CryptoStream crypoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
crypoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
crypoStream.FlushFinalBlock();
crypoStream.Close();
byte[] encryptedBytes = memoryStream.ToArray();
memoryStream.Close();
string encryptedText = Convert.ToBase64String(encryptedBytes);
return encryptedText;
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
}
private string AES256DecryptString(string key, string encryptedText)
{
try
{
using (RijndaelManaged rijndael = new RijndaelManaged())
{
rijndael.KeySize = 256;
rijndael.BlockSize = 128;
rijndael.Key = Encoding.UTF8.GetBytes(key);
byte[] encryptedTextBytes = Encoding.UTF8.GetBytes(encryptedText);
byte[] iv = new byte[16];
Array.Copy(encryptedTextBytes, iv, iv.Length);
rijndael.IV = iv;
rijndael.Mode = CipherMode.CBC;
rijndael.Padding = PaddingMode.PKCS7;
ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV);
MemoryStream memoryStream = new MemoryStream();
byte[] encryptedTextWithoutIVBytes = new byte[encryptedTextBytes.Length - iv.Length];
Array.Copy(encryptedTextBytes, 16, encryptedTextWithoutIVBytes, 0, encryptedTextWithoutIVBytes.Length);
memoryStream.Write(encryptedTextWithoutIVBytes, 0, encryptedTextWithoutIVBytes.Length);
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
StreamReader streamReader = new StreamReader(cryptoStream);
string decryptedText = streamReader.ReadToEnd();
cryptoStream.FlushFinalBlock();
cryptoStream.Close();
memoryStream.Close();
return decryptedText;
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
}
As you can see I add the initialization vector before the encrypted string before adding the encrypted bit because I know that IVs should be random and I've seen this is a good strategy to use. I make sure to remove the IV before decrypting.
Is there a way to fix this without changing the padding mode (I've seen that PKCS7 padding is very secure)?
Problems:
You should use a proper password-based KDF for passwords and similar low-entropy keys. .NET has the Rfc2898DeriveBytes (PBKDF2) class to make this relatively easy.
You are not base64 decoding your ciphertext in your decryptor. Instead of
byte[] encryptedTextBytes = Encoding.UTF8.GetBytes(encryptedText);
you should have
byte[] encryptedTextBytes = Convert.FromBase64String(encryptedText);
You need to reset the position of the MemoryStream after you populate it with ciphertext bytes. After
memoryStream.Write(encryptedTextWithoutIVBytes, 0, encryptedTextWithoutIVBytes.Length);
you need to insert
memoryStream.Seek(0, SeekOrigin.Begin);

Encryption method for using AES-256 symmetric algorithm (AES/ECB/PKCS7Padding) in c#

I want to encrypt a string using AES 256-bit encryption algorithm with ECB and PKCS7Padding. I had gone through many sites but none of them were suitable.
Please suggest a solution
public static string Encrypt(string PlainText, string Password,
string Salt = "Kosher", string HashAlgorithm = "SHA1",
int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
int KeySize = 256)
{
if (string.IsNullOrEmpty(PlainText))
return "";
byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
RijndaelManaged SymmetricKey = new RijndaelManaged();
SymmetricKey.Mode = CipherMode.CBC;
byte[] CipherTextBytes = null;
using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes))
{
using (MemoryStream MemStream = new MemoryStream())
{
using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
{
CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
CryptoStream.FlushFinalBlock();
CipherTextBytes = MemStream.ToArray();
MemStream.Close();
CryptoStream.Close();
}
}
}
SymmetricKey.Clear();
return Convert.ToBase64String(CipherTextBytes);
}
source
Note: you can change the mode by changing SymmetricKey.Mode = CipherMode.CBC;
and you can add SymmetricKey.Padding = PaddingMode.PKCS7; for padding
with bounty castle you should be able to do this :
cipher = CipherUtilities.GetCipher("AES/ECB/PKCS7");
cipher.Init(false, new KeyParameter(key));

My AES Intitialization Vectors seem to do nothing with AESCryptoServiceProvider in C#

I have been doing some research into using AESCryptoServiceProvider in C#. So far, I have an implementation which seems to be working. However, my understanding of initialization vectors is that it should protect my cipher text from being the same when the payload and key are the same, but it appears I am doing something wrong. Changing my initialization vector doesn't seem to affect the results of these functions at all.
Here are my functions:
public string EncryptString(string toEncrypt, byte[] encryptionKey, byte[] iv)
{
var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
//aes.GenerateIV();
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(aes.IV, 0, aes.IV.Length);
bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return Convert.ToBase64String(ms.ToArray());
}
}
}
public string DecryptString(string toDecrypt, byte[] encryptionKey, byte[] iv)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
var toDecryptBytes = Convert.FromBase64String(toDecrypt);
Array.Copy(toDecryptBytes, 0, iv, 0, iv.Length);
aes.IV = iv;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binWriter = new BinaryWriter(cs))
{
binWriter.Write(toDecryptBytes, iv.Length, toDecryptBytes.Length - iv.Length);
}
return Encoding.Default.GetString(ms.ToArray());
}
}
}
New version of my encrypt function I have now following the advice from #owlstead, changing back CBC mode. This does appear to be working correctly now using CBC.
public string EncryptString(string toEncrypt, byte[] encryptionKey)
{
var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
using (var ms = new MemoryStream())
{
ms.Write(aes.IV, 0, aes.IV.Length);
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return Convert.ToBase64String(ms.ToArray());
}
}
}
You are putting the CryptoStream and the BinaryWriter in the wrong order for at least the encrypt function. You are currently encrypting your IV, while the IV should be put in front of the ciphertext, in plain.
The IV is XORed with the plain text before encryption. Now, this means that you zero out the plain text: you are encrypting a zero valued block.
CipherMode.ECB mode seems not picking up the IV argument(which means no matter what IV you passed in, the result will be the same),this is possible because ECB doesn't need a concept as Initial Vector,for there's no subsequent vectors either. If you want to mask your ECB block, XOR your block mannually before encryption is OK.
While you changed to CipherMode.CBC, the IV does matters,for subsequent blocks will be XORed with previous encrypted block ,as forms a chain.

What's wrong with my AES encryption function in C#? Why does it return zero length data?

public static byte[] AES_Encrypt(byte[] data, string[] aes_key)
{
var aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Encoding.Default.GetBytes(aes_key[0]);
aes.IV = Encoding.Default.GetBytes(aes_key[1]);
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write);
cs.Write(data, 0, data.Length);
return ms.ToArray();
}
public static byte[] AES_Decrypt(byte[] data, string[] aes_key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Encoding.Default.GetBytes(aes_key[0]);
aes.IV = Encoding.Default.GetBytes(aes_key[1]);
var decrypt = aes.CreateDecryptor();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write);
cs.Write(data, 0, data.Length);
return ms.ToArray();
}
Somewhere in code:
string key = "788b0adbcf8b9211282fe613b18630d2";
string iv = "7fbb16b806fcc24396653b3218552d39";
byte[] test_byte = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
byte[] enc_byte = AES_Encrypt(test_byte, new string[] { key, iv }); //getting zero array
byte[] dec_byte = AES_Decrypt(enc_byte, new string[] { key, iv });
You need using blocks around your streams to ensure they are closed and flushed. Try this:
public static byte[] AES_Encrypt(byte[] data, string[] aes_key)
{
var aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Encoding.Default.GetBytes(aes_key[0]);
aes.IV = Encoding.Default.GetBytes(aes_key[1]);
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
using(MemoryStream ms = new MemoryStream())
{
using(CryptoStream cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
cs.Write(data, 0, data.Length);
return ms.ToArray();
}
}
public static byte[] AES_Decrypt(byte[] data, string[] aes_key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Encoding.Default.GetBytes(aes_key[0]);
aes.IV = Encoding.Default.GetBytes(aes_key[1]);
var decrypt = aes.CreateDecryptor();
using(MemoryStream ms = new MemoryStream())
{
using(CryptoStream cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
cs.Write(data, 0, data.Length);
return ms.ToArray();
}
}
If you use using statements with your CryptoStreams, it will flush and close so that it actually updates your MemoryStreams. The documentation for CryptoStream documents this behavior: you should always Close when you're done writing to it. Here's how that looks (very similar for encrypt and decrypt):
using (CryptoStream cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
cs.Write(data, 0, data.Length);
return ms.ToArray();
Call FlushFinalBlock before converting the stream to array.

DES ECB C# encryption/decryption

I have a problem with decryption of data previously encrypted. I am using the sequential encrypt-decrypt-encrypt with three different keys to get triple des effect. The encryption function works correctly (returns 8-byte array), but the decryption function returns empty array.
public static byte[] EncryptDES(byte[] clearData, byte[] key)
{
DES desEncrypt = new DESCryptoServiceProvider();
desEncrypt.Mode = CipherMode.ECB;
desEncrypt.Key = key;
ICryptoTransform transForm = desEncrypt.CreateEncryptor();
MemoryStream encryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(encryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(clearData, 0, clearData.Length);
byte [] encryptedData = encryptedStream.ToArray();
return encryptedData;
}
public static byte[] DecryptDES(byte[] clearData, byte[] key)
{
DES desDecrypt = new DESCryptoServiceProvider();
desDecrypt.Mode = CipherMode.ECB;
desDecrypt.Key = key;
ICryptoTransform transForm = desDecrypt.CreateDecryptor();
MemoryStream decryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(decryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(clearData, 0, clearData.Length);
byte[] encryptedData = decryptedStream.ToArray();
return encryptedData;
}
public static byte[] Encrypt3DES(byte[] clearData, byte[] key0, byte[] key1, byte[] key2)
{
byte[] encryptedData1 = new byte[clearData.Length];
byte[] encryptedData2 = new byte[clearData.Length];
byte[] encryptedData3 = new byte[clearData.Length];
encryptedData1 = DESCrypto.EncryptDES(clearData , key0);
encryptedData2 = DESCrypto.DecryptDES(encryptedData1, key1);
encryptedData3 = DESCrypto.EncryptDES(encryptedData2, key2);
return encryptedData3;
}
What am I doing wrong?
TripleDES already exist in the Framework but I guess you want to roll your own implementation for educational purposes.
You're making things more complicated than necessary. Since you are using streams why don't you chain them all instead:
public static byte[] TripleDESEncrypt(byte[] plainText, byte[] key1, byte[] key2, byte[] key3)
{
var des = DES.Create();
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
des.Key = key3;
var encryptor1 = des.CreateEncryptor();
des.Key = key2;
var decryptor = des.CreateDecryptor();
des.Padding = PaddingMode.PKCS7;
des.Key = key1;
var encryptor2 = des.CreateEncryptor();
byte[] result;
using (var ms = new MemoryStream())
{
using (var cs1 = new CryptoStream(ms, encryptor1, CryptoStreamMode.Write))
using (var cs2 = new CryptoStream(cs1, decryptor, CryptoStreamMode.Write))
using (var cs3 = new CryptoStream(cs2, encryptor2, CryptoStreamMode.Write))
cs3.Write(plainText, 0, plainText.Length);
result = ms.ToArray();
}
return result;
}
 
public static byte[] TripleDESDecrypt(byte[] cipherText, byte[] key1, byte[] key2, byte[] key3)
{
var des = DES.Create();
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.PKCS7;
des.Key = key1;
var decryptor1 = des.CreateDecryptor();
des.Padding = PaddingMode.None;
des.Key = key2;
var encryptor = des.CreateEncryptor();
des.Key = key3;
var decryptor2 = des.CreateDecryptor();
byte[] result;
using (var ms = new MemoryStream())
{
using (var cs1 = new CryptoStream(ms, decryptor1, CryptoStreamMode.Write))
using (var cs2 = new CryptoStream(cs1, encryptor, CryptoStreamMode.Write))
using (var cs3 = new CryptoStream(cs2, decryptor2, CryptoStreamMode.Write))
cs3.Write(cipherText, 0, cipherText.Length);
result = ms.ToArray();
}
return result;
}
Make note of the usage of using blocks and also how the padding is applied to the different streams.
The framework TripleDES is about 2.5 times faster than the above code.
public static byte[] TripleDESEncryptFramework(byte[] plainText, byte[] key)
{
var tdes = TripleDES.Create();
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
tdes.Key = key;
var encryptor = tdes.CreateEncryptor();
byte[] result;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
cs.Write(plainText, 0, plainText.Length);
result = ms.ToArray();
}
return result;
}
If you want to compare the results of the two different encryption methods then you need to remember that the 24-bit key for TripleDES is actually the 3 keys put in one array:
[ key1 ][ key2 ][ key3 ]
==============================
[ key ]
Just need cryptoStream.FlushFinalBlock(). Its code works great:
//ENCRYPT
public static byte[] EncryptDES(byte[] clearData, byte[] key)
{
DES desEncrypt = new DESCryptoServiceProvider();
desEncrypt.Mode = CipherMode.ECB;
desEncrypt.Key = key;
ICryptoTransform transForm = desEncrypt.CreateEncryptor();
MemoryStream encryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(encryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(clearData, 0, clearData.Length);
cryptoStream.FlushFinalBlock();
return encryptedStream.ToArray();
}
//DECRYPT
public static byte[] DecryptDES(byte[] clearData, byte[] key)
{
DES desDecrypt = new DESCryptoServiceProvider();
desDecrypt.Mode = CipherMode.ECB;
desDecrypt.Key = key;
ICryptoTransform transForm = desDecrypt.CreateDecryptor();
MemoryStream decryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(decryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(clearData, 0, clearData.Length);
cryptoStream.FlushFinalBlock();
return decryptedStream.ToArray();
}

Categories