I'm using the code bellow to Encrypt and Decrypt a file:
// Encryption
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes("password");
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("FileName", FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
// Decryption
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes("password");
FileStream fsCrypt = new FileStream("filename", FileMode.Open);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateDecryptor(key, key),
CryptoStreamMode.Read);
int data;
while ((data = cs.ReadByte()) != -1)
memorystream.WriteByte((byte)data);
It works well, without any problem!
For some reasons I've added 10 bytes at the first of the encrypted file!
Actually I've created a 10 bytes file (the file size is EXACTLY 10 Bytes), then I've appended the encrypted file to this file.
Note that the 10 bytes file is not encrypted, and is created using simple filestream, and it could be read in notepad.
Now in decryption code, how could I eliminate the first 10 bytes and decrypt remain data in the file?
I've tried to call ReadByte() for 10 times, and then goto the WHILE part and decrypt file, but it doesn't work and I get length invalid exception.
Thanks in advance.
Posting my comment as an answer as requested.
You say
I've tried to call ReadByte() for 10 times, and then goto the WHILE
part and decrypt file, but it doesn't work and I get length invalid
exception.
but you don't say exactly which stream you call ReadByte() 10 times on, and when exactly you call it.
Make sure you're calling it on fsCrypt before you instantiate the new CryptoStream.
Note also that that you can probably just call fsCrypt.Position = 10; or fsCrypt.Seek(10, SeekOrigin.Begin), instead of reading 10 dummy bytes, since FileStream supports seeking.
For example:
byte[] key = Encoding.Unicode.GetBytes("password");
FileStream fsCrypt = File.OpenRead("filename");
fsCrypt.Position = 10; // Skip the 10 useless bytes at the start
RijndaelManaged rijndaelManaged = new RijndaelManaged();
// Disposing CryptoStream will also dispose the FileStream passed to it
using (CryptoStream cryptoStream = new CryptoStream(fsCrypt, rijndaelManaged.CreateDecryptor(key, key), CryptoStreamMode.Read))
{
cryptoStream.CopyTo(memoryStream);
}
As an aside: DO NOT USE THE KEY AS THE IV!
The IV needs to be different every time you encrypt something, but can be public. A common technique is to randomly generate an IV every time you want to encrypt something, and put it as the first N bytes of the encrypted stream. When it comes to decrypting, read the first N bytes of the encrypted stream yourself (before creating the CryptoStream), set that as the IV, open the CryptoStream, and read the rest.
Related
To decrease memory usage when returning a file to a client, whilst decrypting, we have gone with streams.
This was working fine until one quirk which is that when you upload the same file back to the server (e.g. when a client modifies it). It causes .net core to throw "the process cannot access the file path because it is being used by another process".
As this system is still in development, I'm unsure whether it's a quirk of running the application in debug mode rather than release. Although I built the code into release and still received the same error.
From what I'm aware of how return a stream works, it should dispose the streams automatically.
The first method that creates a stream contains the following:
return (await Decrypt(File.OpenRead(path), AesKey, AesIv), contentType);
The decrypt method then performs the following:
public static async Task<MemoryStream> Decrypt(FileStream data, string key, string iv)
{
Aes aes = Aes.Create();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.Zeros;
aes.Key = Encoding.ASCII.GetBytes(key);
aes.IV = Encoding.ASCII.GetBytes(iv);
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
return await PerformCryptography(data, decryptor);
}
This then calls the crypto method:
private static async Task<MemoryStream> PerformCryptography(FileStream data, ICryptoTransform cryptoTransform)
{
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write);
await data.CopyToAsync(cryptoStream);
cryptoStream.FlushFinalBlock();
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
This returns back up the chain to the controller that returns the following:
return File(file, contentType, fileName);
When I was developing this, it seemed wrapping any of these in using would cause an object disposed exception, however I may have done something wrong.
Does anyone know how to fix something like this?
You need to dispose the FileStream after you've finished copying its contents to the CryptoStream. An easy way to do this is with a using block inside Decrypt. Note that the await is inside the using block, so the FileStream won't be disposed until the await completes:
public static async Task<MemoryStream> Decrypt(FileStream data, string key, string iv)
{
using (data)
{
Aes aes = Aes.Create();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.Zeros;
aes.Key = Encoding.ASCII.GetBytes(key);
aes.IV = Encoding.ASCII.GetBytes(iv);
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
return await PerformCryptography(data, decryptor);
}
}
Another approach is to open the CryptoStream in Read mode over the FileStream. Instead of copying data into the CryptoStream (and the CryptoStream then writes that data to the MemoryStream), you read data out of the CryptoStream (and the CryptoStream pulls data from the FileStream as it needs):
private static async Task<MemoryStream> PerformCryptography(FileStream data, ICryptoTransform cryptoTransform)
{
MemoryStream memoryStream = new MemoryStream();
using (CryptoStream cryptoStream = new CryptoStream(fileStream, cryptoTransform, CryptoStreamMode.Read))
{
await cryptoStream.CopyToAsync(memoryStream);
}
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
This lets you dispose the CryptoStream (which you should be disposing, because it's IDisposable) instead of calling FlushFinalBlock(), which is a bit neater. Disposing it also disposes the underlying FileStream.
If you're in a situation where you don't need to seek on the Stream which Decrypt returns (sadly this isn't the case if you're returning it from ASP.NET Core with File(...)), you can just return the CryptoStream. This means that the decryption happens as that stream is read, rather than upfront, which may not be what you want:
public static Stream Decrypt(FileStream data, string key, string iv)
{
Aes aes = Aes.Create();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.Zeros;
aes.Key = Encoding.ASCII.GetBytes(key);
aes.IV = Encoding.ASCII.GetBytes(iv);
return new CryptoStream(data, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read);
}
I don't like how you're deriving your key and IV. ASCII only supports values 0 to 127, so your keyspace is only half the size it should be: you're effectively using AES-128 rather than AES-256. Likewise your IV is half the size it should be. Use a key derivation function like PBKDF2 to turn a (long) plain-text key into a proper 256-bit binary key.
Your IV is also suspect. Remember that everything you encrypt using a particular key needs to have a unique IV. That's very important. Do not re-use IVs!! It's common practice to randomly generate an IV when encrypting something, and to write that as the first few bytes of the ciphertext (it's fine if the IV is public, it just has to be unique). You can then extract that when decrypting.
Your padding is also slightly suspect: it means that any trailing 0's in the plaintext are removed by the encryption/decryption process. It's much better to use something like PKCS7.
I am working in a C# application. We have common methods to store data on a file. These methods encrypt the data and store them on the file system. when we need the data, ReadData method decrypts the data and returns me plain text.
This code works fine in normal cases if size of the text in small. but for a example text given below, the decryption code is throwing exception - length of the data to decrypt is invalid.
The exception occurs at line
// close the CryptoStream
x_cryptostream.Close();
I tried different ways but no luck. Can some pls help.
Why am I encrypting already encrypted data - I am just trying to store in a file using common method of the huge application. The common methods storedata(key,data) nad readdata(key) do the encryption/decryption I can't avoid.
public static byte[] Decrypt(byte[] ciphertext, string Key, string IV)
{
byte[] k = Encoding.Default.GetBytes(Key);
byte[] iv = Encoding.Default.GetBytes(IV);
// create the encryption algorithm
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
x_alg.Padding = PaddingMode.PKCS7;
// create an ICryptoTransform that can be used to decrypt data
ICryptoTransform x_decryptor = x_alg.CreateDecryptor(k, iv);
// create the memory stream
MemoryStream x_memory_stream = new MemoryStream();
// create the CryptoStream that ties together the MemoryStream and the
// ICryptostream
CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
x_decryptor, CryptoStreamMode.Write);
// write the ciphertext out to the cryptostream
x_cryptostream.Write(ciphertext, 0, ciphertext.Length);
// close the CryptoStream
x_cryptostream.Close();
// get the plaintext from the MemoryStream
byte[] x_plaintext = x_memory_stream.ToArray();
Below is the code of encrypt method.
public static byte[] Encrypt(string strplain, string Key, string IV)
{
byte[] k = Encoding.Default.GetBytes(Key);
byte[] iv = Encoding.Default.GetBytes(IV);
byte[] plaintext = Encoding.Default.GetBytes(strplain);
// create the encryption algorithm
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
x_alg.Padding = PaddingMode.PKCS7;
// create an ICryptoTransform that can be used to encrypt data
ICryptoTransform x_encryptor = x_alg.CreateEncryptor(k, iv);
// create the memory stream
MemoryStream x_memory_stream = new MemoryStream();
// create the CryptoStream that ties together the MemoryStream and
// the ICryptostream
CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
x_encryptor, CryptoStreamMode.Write);
// write the plaintext out to the cryptostream
x_cryptostream.Write(plaintext, 0, plaintext.Length);
// close the CryptoStream
x_cryptostream.Close();
// get the ciphertext from the MemoryStream
byte[] x_ciphertext = x_memory_stream.ToArray();
// close memory stream
x_memory_stream.Close();
// convert from array to string
string cipher_Tx = Encoding.Default.GetString(x_ciphertext,
0, x_ciphertext.Length);
x_encryptor.Dispose();
x_alg.Clear();
byte[] cipher = Encoding.Default.GetBytes(cipher_Tx);
return cipher;
}
Your problem is string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 0, x_ciphertext.Length);.
x_ciphertext is not a valid byte representation of text, it has many unpresentable characters and when you do your byte[] to string conversion you are losing information. The correct way to do it is use a string format that is designed to represent binary data using something like Convert.ToBase64String(byte[]) and Convert.FromBase64String(string).
string cipher_Tx = Convert.ToBase64String(x_ciphertext)
x_encryptor.Dispose();
x_alg.Clear();
byte[] cipher = Convert.FromBase64String(cipher_Tx)
That being said, there is a lot of other "odd" things about your code, for example you don't use using statements and you really should. Also that whole conversion to string and back is totally unnecessary, just return x_ciphertext. There may be other problems with the code too (like where did the strings for Key and IV come from) and many other best practices (like you should be generating a random IV and writing it out in to the output and the key should be generated using a key derivation function not straight from user text), but I stopped checking after I found the string conversion issue.
Your code above works as long as the key and iv used to decrypt match the key and iv used to encrypt. Try this:
byte[] test = new byte[1000000];
for (int i = 0; i < 256; i++)
{
test[i] = (byte)i;
}
var ciphertext = Encrypt(Encoding.Default.GetString(test), "0000000000000000", "0000000000000000");
byte[] check = Decrypt(ciphertext, "0000000000000000", "0000000000000000");
for (int i = 0; i < 256; i++)
{
Debug.Assert(check[i] == (byte)i, "round trip");
}
As you can see, one million bytes encrypt and decrypt just fine with your code, so I don't think it has anything to do with data size.
However, change the IV like this:
byte[] check = Decrypt(ciphertext, "0000000000000000", "000000000000000X"); // note X
and the Debug.Assert will fire -- the decryption will not match. However, x_cryptostream.Close() succeeds.
Next, try changing the key like this:
byte[] check = Decrypt(ciphertext, "000000000000000X", "0000000000000000"); // note X
Now, x_cryptostream.Close() will fail with a CryptographicException, probably, "Padding is invalid and cannot be removed."
Corrupting the key will cause the decryption to fail, and x_cryptostream.Close() to fail.
I think the problem is in your saving and later restoring the key bytes.
BTW: Hopefully you are using the full binary range of the key, and not basing it only on ASCII characters, otherwise you don't really have a strong key.
I'm trying to encrypt and then decrypt a file when needed, and read the file for use of the program. Still learning cryptography and I looked at several examples and tried this.
I have managed to en- and decrypt string but sadly that did not work in this case.
Cryptostream gives an IV does not match block size error on encrypt. Could not even have tested decrypting but that is most likely off too.
So question is: How can I encrypt, decrypt and read a .txt file?
private void EncryptFile(string resultFile)
{
string password = #"test";
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsCrypt = File.Create(resultFile);
RijndaelManaged rmCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt, rmCrypto.CreateEncryptor(key, key),CryptoStreamMode.Write);
StreamWriter sWriter = new StreamWriter(cs);
sWriter.WriteLine(resultFile);
sWriter.Close();
cs.Close();
fsCrypt.Close();
}
private void DecryptFile(string resultFile)
{
string password = #"test";
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsCrypt = new FileStream(resultFile, FileMode.Open);
RijndaelManaged rmCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt, rmCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read);
StreamWriter sWriter = new StreamWriter(cs);
sWriter.WriteLine(resultFile);
sWriter.Close();
cs.Close();
fsCrypt.Close();
}
I would recommend using AesManaged instead of RijndaelManaged, mostly because the former follows a defined standard.
Aes/rijndael have specified sizes for the key and initialization vector. You should not use bytes from a password directly. The correct way is to run the password thru a key derivation function see keyderivationalgorithmprovider, to ensure you have a key of the correct size and of good quality. Or use a randomly generated binary key of the correct size.
The second problem is the Initialization vector, this also need to be of a the correct size and should be random. But the IV is not secret and will usually be attached to the encrypted message, so using the key as the IV will not be secure.
The best option is probably to use the key and IV from the encryption class. See microsofts example for more details.
I have searched online but have not been able to find any solutions to my problem.
I am using previously written methods to encrypt and ecrypt text using the Rijndael class.
I use these functions to encrypt and decrypt usernames and emails for a web application I have been working on.
The encryption/decryption works perfectly, but every once in a while I get this error:
System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.
Currently, I am getting this error with a specific email address and I can't reproduce the error even if I replace some of the letters in the email.
Here are the encryption/decrytpion functions. The IV and Key are defined as read only strings.
static public string Encrypting(string Source)
{
byte[] bytIn = System.Text.ASCIIEncoding.ASCII.GetBytes(Source);
// create a MemoryStream so that the process can be done without I/O files
System.IO.MemoryStream ms = new System.IO.MemoryStream();
byte[] IVBytes = Encoding.ASCII.GetBytes(IV);
byte[] KEYBytes = Encoding.ASCII.GetBytes(KEY);
Rijndael rijndael = Rijndael.Create();
rijndael.IV = IVBytes;
rijndael.Key = KEYBytes;
// create Crypto Stream that transforms a stream using the encryption
CryptoStream cs = new CryptoStream(ms, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
// write out encrypted content into MemoryStream
cs.Write(bytIn, 0, bytIn.Length);
cs.FlushFinalBlock();
// get the output and trim the '\0' bytes
byte[] bytOut = ms.GetBuffer();
int i = 0;
for (i = 0; i < bytOut.Length; i++)
if (bytOut[i] == 0)
break;
// convert into Base64 so that the result can be used in xml
return System.Convert.ToBase64String(bytOut, 0, i);
}
static public string Decrypting(string Source)
{
// convert from Base64 to binary
byte[] bytIn = System.Convert.FromBase64String(Source);
// create a MemoryStream with the input
System.IO.MemoryStream ms = new System.IO.MemoryStream(bytIn, 0, bytIn.Length);
byte[] IVBytes = Encoding.ASCII.GetBytes(IV);
byte[] KEYBytes = Encoding.ASCII.GetBytes(KEY);
Rijndael rijndael = Rijndael.Create();
rijndael.IV = IVBytes;
rijndael.Key = KEYBytes;
// create Crypto Stream that transforms a stream using the decryption
CryptoStream cs = new CryptoStream(ms, rijndael.CreateDecryptor(), CryptoStreamMode.Read);
// read out the result from the Crypto Stream
System.IO.StreamReader sr = new System.IO.StreamReader(cs);
return sr.ReadToEnd();
}
FYI - I am very new to cryptography and security.
Can these functions be fixed to avoid special cases that cause the error, or should I scrap these and use the RijndaelManaged class?
Sites I found that use RijndaelManaged:
SeeSharp
TekEye
The issue is almost certainly nothing to do with Rijndael vs. RijndaelManaged (or any other such implementation), but instead because the encrypted data contains a 0x00, and you are incorrectly assuming that the the ciphertext ends at the first 0x00 byte. Since the ciphertext can legitimately contain any byte value you should instead use the stream's Length property to determine the length of the ciphertext.
Eliminate the section you've commented: "get the output and trim the '\0' bytes" and replace the return ... statement with:
return System.Convert.ToBase64String(ms.GetBuffer(), 0, ms.Length);
It should be noted that there are many other issues with your use of cryptography here, e.g. the use of a key generated directly from the ASCII encoding of a string, and the fact you're using a fixed IV both negatively impact security.
The norm for the error is a padding issue. What version of .NET are you using? It is more common to use the AES classes (AES, or Advanced Encryption Standard, which is Rijndael). There are plenty of AES implementations you can find as samples.
If you need some proof AES is Rijndael: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
I'm trying to make an encryption system with c#. This is the code for the encryption.
public static void EncryptFile(string inFile, string outFile, string #inkey)
{
try
{
UnicodeEncoding ue = new UnicodeEncoding();
byte[] key = ue.GetBytes(inkey);
FileStream fsEncrypt = new FileStream(outFile, FileMode.Create);
RijndaelManaged rmCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsEncrypt, rmCrypto.CreateEncryptor(key, key), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inFile, FileMode.Open);
int data;
while((data=fsIn.ReadByte()) != 1){
cs.WriteByte((byte)data);
}
fsIn.Close(); cs.Close(); fsEncrypt.Close();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Fail to encrypt", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Now, this code throws exception every time I run it, says
Specified initialization vector(IV) does not match the block size for
this algorithm
I have read on other discussion about this, saying that there is a problem with the number of bytes (my key length passed into this function is 255). But I have tried making the key only 16 bytes and still not working.
After some troubleshooting I found out that this part:
CryptoStream cs = new CryptoStream(fsEncrypt, rmCrypto.CreateEncryptor(key, key), CryptoStreamMode.Write);
throws the exception. I have no idea why. Anyone can help?
You're passing the key twice to CreateEncryptor, but it needs a key and an IV (Initialization Vector). The second parameter should be an array with 128 random bits. 128 bits is the default block size for RijndaelManaged, but it accepts other values as well (such as 256). Read this for more info. And as Grzegorz W pointed out in the comments, you might need to choose a different key size as well.
If you're not familiar with encryption (in which case you should stop and learn more about it before implementing your own solution, or use a ready-made one instead), the function of the IV is prevent that the same message encoded twice produce the same ciphertext. It should be random for each message (and each use of the message), does not need to be kept secret, but you need to store it to be able to decipher the message later (i.e. you can not discard it after encryption).