while encrypting an audio file using C#, i got an exception that "Specified inetialisation vector(IV) does not match the block size of the algorithm". and i'm using Rijndael algorithm provided by the cryptography class. what i'm supposed to do to solve this exception?
my code is given below:
public void EncryptFile(string inputFile, string outputFile)
{
try
{
inputFile = textBox_path.Text;
String password = "keykey";
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
string cryptFile = outputFile;
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, key), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
MessageBox.Show("encryption is completed!!");
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
}
and my function call is:
There is a similar question here.
With Rijndael, you can choose the block sizes 128, 160, 192, 224, or 256 bits. Then you must choose an initialization vector of the same length:
using (RijndaelManaged rm = new RijndaelManaged())
{
rm.BlockSize = 128;
Rfc2898DeriveBytes keyDerivator = new Rfc2898DeriveBytes(password, salt, KeyGenIterationCount); //derive key and IV from password and salt using the PBKDF2 algorithm
rm.IV = keyDerivator.GetBytes(16); //16 bytes (128 bits, same as the block size)
rm.Key = keyDerivator.GetBytes(32);
//(encrypt here)
}
In any case, I would recommend using the AesCryptoServiceProvider class instead, since it's FIPS-compilant. Read more about the differences here: http://blogs.msdn.com/b/shawnfa/archive/2006/10/09/the-differences-between-rijndael-and-aes.aspx
Related
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);
I didn't want to seed the IV with the user given password. The question is if I've created the method of encrypting with a generated IV, is there anyway I could save the generated IV, to get it into the decryption method, without saving it into a database.
private void EncryptFile(string inputFile,string outputFile, string pass_input){
try
{
MessageBox.Show("FLAG 1");
UTF8Encoding UE = new UTF8Encoding();
//Represents a UTF-8 encoding of unicode characters
string password = pass_input;
// passed password through user input
byte[] key = UE.GetBytes(password);
//password parsed into bytes
string cryptFile = outputFile;
FileStream FScrypt = new FileStream(cryptFile, FileMode.Create);
//FScrypt is created where it creates a file to save output
RijndaelManaged RMcrypto = new RijndaelManaged();
//RMcrypto is created where new instance of RijndaelManaged Class is initialized
RMcrypto.GenerateIV();
CryptoStream cs = new CryptoStream(FScrypt, RMcrypto.CreateEncryptor(key,RMcrypto.IV), CryptoStreamMode.Write);
//CreateEncryptor from RMcrypto creates a symmetric Rijndael Encryptor object with specified key
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
//fsIn Opens the input file
MessageBox.Show("FLAG 2");
int data;
while ((data = fsIn.ReadByte()) != -1)
//will read input file opened by fsIn
cs.WriteByte((byte)data);
MessageBox.Show("FLAG 3");
fsIn.Close();
cs.Close();
FScrypt.Close();
catch(Exception ex)
{
MessageBox.Show("Failed");
}
}
private void DecryptFile(string inputFile, string outputFile, string pass_input)
{
try
{
string password = pass_input; // Taking password input and storing in local string.
UTF8Encoding UE = new UTF8Encoding() //UnicodeEncoding Object UTF8
byte[] key = UE.GetBytes(password); // converting password to bytes
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open); // Opens the file
RijndaelManaged RMCrypto = new RijndaelManaged(); //new RijndaelMaaged object
CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateDecryptor(key, **"RMCrypto.IV"**), CryptoStreamMode.Read);
FileStream fsOut = new FileStream(outputFile, FileMode.Create);
int data;
while ((data = cs.ReadByte()) != -1)
fsOut.WriteByte((byte)data);
fsOut.Close();
cs.Close();
fsCrypt.Close();
}
catch (Exception ex)
{
MessageBox.Show("Failed");
}
}
One well used/accepted method is to prepend the IV to the encrypted data, the IV does not need to be secret.
I'm writing a program at the moment that works under the following scenario:
I've got some confidential log files that I need to backup to a server.
I have a program that generates these log files every day.
These log files would rarely if ever need to be opened.
I have only one RSA public/private key pair.
The program has only the RSA public key.
I generate a random AES key each time the program makes one of these confidential files.
The program uses this AES key to encrypt the log file.
I then use the RSA public key to encrypt the AES Key
I then backup both the AES encrypted file and RSA encrypted AES key to the server.
As far as I understand, that protocol is fitting for my use case.
The issue I'm having is coding it up in C#. I ran into needing an Initialization Vector(IV) for my AES encryption, I tried to encrypt this along with the AES key by using the public RSA key on both. But the 512(2 * 256) size is larger than RSA is happy to encrypt. So I figured out since I created the Initialization Vector randomly each time just like the AES Key, I can add the IV to the front of the AES ciphertext. However, I'm not sure where the code to do this would be inserted in my functions
Any help in the right direction to the "protocol" or other ways to write the IV to the ciphertext would be great. Thank you in advance.
static public Tuple<byte[], byte[]> EncryptAES(byte[] toEncryptAES, RSAParameters RSAPublicKey)
{
byte[] encryptedAES = null;
byte[] encryptedRSA = null;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Mode = CipherMode.CBC;
AES.GenerateIV();
AES.GenerateKey();
encryptedRSA = RSAEncrypt(AES.Key, RSAPublicKey);
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
ms.Write(AES.IV, 0, AES.KeySize); //DOESNT WORK HERE
//Can't use CS to write it to the stream else it will encrypt along with file
cs.Write(toEncryptAES, 0, toEncryptAES.Length);
cs.Close();
}
encryptedAES = ms.ToArray();
}
}
return new Tuple<byte[], byte[]>(encryptedAES, encryptedRSA);
}
static public byte[] DecryptAES(byte[] toDecryptAES, byte[] AESKeyAndIV, RSAParameters RSAPrivateKey)
{
byte[] AESKey = RSADecrypt(AESKeyAndIV, RSAPrivateKey);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Key = AESKey;
ms.Read(AES.IV, 0, AES.KeySize); //Not sure if can read MS here
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
//Would I need to move 0 to 256?
cs.Write(toDecryptAES, 0, toDecryptAES.Length);
cs.Close();
}
return ms.ToArray();
}
}
}
You where quite close, write out the IV before you create the CryptoStream
static public Tuple<byte[], byte[]> EncryptAES(byte[] toEncryptAES, RSAParameters RSAPublicKey)
{
byte[] encryptedAES = null;
byte[] encryptedRSA = null;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Mode = CipherMode.CBC;
AES.GenerateIV();
AES.GenerateKey();
encryptedRSA = RSAEncrypt(AES.Key, RSAPublicKey);
ms.Write(AES.IV, 0, AES.KeySize); //Move the write here.
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(toEncryptAES, 0, toEncryptAES.Length);
cs.Close();
}
encryptedAES = ms.ToArray();
}
}
return new Tuple<byte[], byte[]>(encryptedAES, encryptedRSA);
}
For the decrypt, make sure you loop over the read till you have fully read the byte[] for the IV, Stream.Read is not guaranteed to read all the bytes you asked it to read. I usually make a static method ReadFully to ensure all bytes are read.
private static byte[] ReadFully(Stream stream, int length)
{
int offset = 0;
byte[] buffer = new byte[length];
while(offset < length)
{
offset += stream.Read(buffer, offset, length - offset);
}
return buffer;
}
Then just use that method to read in the IV. You also want to use cs.Read not cs.Write to read out the encrypted data and put the stream in to read mode, however it is easier to just use .CopyTo and copy the data to a new MemoryStream.
static public byte[] DecryptAES(byte[] toDecryptAES, byte[] AESKeyAndIV, RSAParameters RSAPrivateKey)
{
byte[] AESKey = RSADecrypt(AESKeyAndIV, RSAPrivateKey);
using (MemoryStream source = new MemoryStream(toDecryptAES))
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Key = AESKey;
var iv = ReadFully(source, AES.KeySize);
AES.IV = iv;
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(source, AES.CreateDecryptor(), CryptoStreamMode.Read))
{
using(var dest = new MemoryStream())
{
cs.CopyTo(dest);
return dest.ToArray();
}
}
}
}
}
For other readers, note that RSAEncrypt and RSADecrypt are wrappers for calls to the RSACryptoServiceProvider.
I have this code which is meant to decrypt a file, but if I run it, it throws a CryptographicException (length of the data to decrypt is invalid) at the end of the using statement using (CryptoStream ...) { ... }
public static void DecryptFile(string path, string key, string saltkey, string ivkey)
{
try
{
byte[] cipherTextBytes;
using (StreamReader reader = new StreamReader(path)) cipherTextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
byte[] keyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] plainTextBytes;
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
plainTextBytes = new byte[Encoding.UTF8.GetByteCount((new StreamReader(cryptoStream)).ReadToEnd())];
cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
//plainTextBytes = memoryStream.ToArray();
cryptoStream.FlushFinalBlock();
}
}
string result = Encoding.ASCII.GetString(plainTextBytes, 0, plainTextBytes.Length).TrimEnd("\0".ToCharArray());
using (FileStream writer = new FileStream(path, FileMode.Create)) writer.Write(Encoding.ASCII.GetBytes(result), 0, Encoding.ASCII.GetBytes(result).Length);
MessageBox.Show("Decrypt succesfull");
}
catch (Exception ex)
{
MessageBox.Show("An error while decrypting the file:\n\n" + ex, "Error");
}
}
}
Does anybody know why this is or how I can fix it? (I don't know if it comes from my encrypting method, but I have another program which uses the exact same thing to encrypt strings and that one does work.)
My encrypting method:
public static void EncryptFile(string path, string key, string saltkey, string ivkey)
{
try
{
byte[] TextBytes;
using (StreamReader reader = new StreamReader(path)) TextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
byte[] KeyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(KeyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] CipherTextBytes;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(TextBytes, 0, TextBytes.Length);
cs.FlushFinalBlock();
CipherTextBytes = ms.ToArray();
}
}
using (FileStream writer = new FileStream(path, FileMode.Create)) writer.Write(CipherTextBytes, 0, CipherTextBytes.Length);
MessageBox.Show("Encrypt succesfull");
}
catch (Exception ex)
{
MessageBox.Show("An error while encrypting the file:\n\n" + ex, "Error");
}
}
There are a few issues with your code:
You use a padding mode of Zeroes in Encrypt and None in Decrypt. These need to match
You load the bytes from your file using Encoding.UTF8, you need to read the raw bytes, you can do this by using the following instead:
byte[] cipherTextBytes = File.ReadAllBytes(path);
You call cryptoStream.FlushFinalBlock(); when only using a single iteration of a stream. You don't need this call in Decrypt if you are only doing a single block iteration.
You read the original text from your file in UTF8 and then write it back as ASCII. You should either change the result assignment in decrypt to use UTF8 or (preferably) change both to use raw bytes.
You use Create to interact with the files when you are overwriting in-place. If you know the file already exists (as you are replacing it) you should use truncate or better yet just call File.WriteAllBytes.
Your decrypt is all kinds of messed up. It looks like you're tying yourself into knots over byte retrieval. You should just use the raw bytes out of the CryptoStream and not try using UTF8
Here's a revised set of methods for you:
public static void DecryptFile(string path, string key, string saltkey, string ivkey)
{
byte[] cipherTextBytes = File.ReadAllBytes(path);
byte[] keyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CFB, Padding = PaddingMode.PKCS7 };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] plainTextBytes;
const int chunkSize = 64;
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
using (MemoryStream dataOut = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var decryptedData = new BinaryReader(cryptoStream))
{
byte[] buffer = new byte[chunkSize];
int count;
while ((count = decryptedData.Read(buffer, 0, buffer.Length)) != 0)
dataOut.Write(buffer, 0, count);
plainTextBytes = dataOut.ToArray();
}
File.WriteAllBytes(path, plainTextBytes);
}
and:
public static void EncryptFile(string path, string key, string saltkey, string ivkey)
{
byte[] TextBytes = File.ReadAllBytes(path);
byte[] KeyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CFB, Padding = PaddingMode.PKCS7 };
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(KeyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] CipherTextBytes;
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(TextBytes, 0, TextBytes.Length);
cs.FlushFinalBlock();
CipherTextBytes = ms.ToArray();
}
File.WriteAllBytes(path, CipherTextBytes);
}
Most likely your problem comes from cipherTextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
You can't use UTF8 to encode arbitrary binary data, you will likely need to fix both your encrypting end decrypting end. You either must use cipherTextBytes = File.ReadAllBytes(path) or if you are forced to use strings you must first encode the bytes to a valid string using Convert.ToBase64String()
In my case it happened because I was decrypting a value which was never encrypted.
I had my values saved in the database without encryption. But when I introduced encryption and decryption routine in my code and executed my program first time, it was actually trying to decrypt a value which was never encrypted, hence the problem.
Simply clearing the existing values from the database for the initial run solved the problem. If you don't want to lose data even during the first run then you should write a separate routine to encrypt the existing values.
I can encrypt image file. but can not decrypt that file.
while ((readLen = cryptStrm.Read(bs, 0, bs.Length)) > 0)
anyone can guess which part is wrong?
the code i programmed is the following.
when i read encrypted file, i can not read at all.
and the CryptoStream's property called length and position has "NotSupportedException",
when i see cryptstream's property using vss.
I waste many hours to solve this problem.....
Pls help me.....
Encrypt
[Bitmap >> encrypted fie]
Decrypt
[encrypted fie >> file]
Encrypt
public static void EncryptFile(
Bitmap bmp, string destFile, byte[] key, byte[] iv)
{
System.Security.Cryptography.RijndaelManaged rijndael =
new System.Security.Cryptography.RijndaelManaged();
rijndael.Key = key;
rijndael.IV = iv;
System.IO.FileStream outFs = new System.IO.FileStream(
destFile, System.IO.FileMode.Create, System.IO.FileAccess.Write);
System.Security.Cryptography.ICryptoTransform encryptor =
rijndael.CreateEncryptor();
System.Security.Cryptography.CryptoStream cryptStrm =
new System.Security.Cryptography.CryptoStream(
outFs, encryptor,
System.Security.Cryptography.CryptoStreamMode.Write);
MemoryStream ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Jpeg);
byte[] bs = new byte[1024];
int readLen;
while ((readLen = ms.Read(bs, 0, bs.Length)) > 0)
{
cryptStrm.Write(bs, 0, readLen);
}
ms.Close();
cryptStrm.Close();
encryptor.Dispose();
outFs.Close();
}
Decrypt
public static void DecryptFile(
string sourceFile, string destFile, byte[] key, byte[] iv)
{
System.Security.Cryptography.RijndaelManaged rijndael =
new System.Security.Cryptography.RijndaelManaged();
rijndael.Key = key;
rijndael.IV = iv;
System.IO.FileStream inFs = new System.IO.FileStream(
sourceFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
System.Security.Cryptography.ICryptoTransform decryptor =
rijndael.CreateDecryptor();
System.Security.Cryptography.CryptoStream cryptStrm =
new System.Security.Cryptography.CryptoStream(
inFs, decryptor,
System.Security.Cryptography.CryptoStreamMode.Read);
System.IO.FileStream outFs = new System.IO.FileStream(
destFile, System.IO.FileMode.Create, System.IO.FileAccess.Write);
byte[] bs = new byte[1024];
int readLen;
while ((readLen = cryptStrm.Read(bs, 0, bs.Length)) > 0)
{
outFs.Write(bs, 0, readLen);
}
outFs.Close();
cryptStrm.Close();
decryptor.Dispose();
inFs.Close();
}
Try setting the Mode and Padding members of rijndael.
The default padding mode caused problems when I did a similar implementation.
// It is reasonable to set encryption mode to Cipher Block Chaining
// (CBC). Use default options for other symmetric key parameters.
rijndael.Mode = CipherMode.CBC;
rijndael.Padding = PaddingMode.None;