C# Encryption System.Security.Cryptography.CryptographicException at CryptoStream.close() - c#

im trying to write an En-/Decrypter in C#. As the title suggests I get a System.Security.Cryptography.CryptographicException at CryptoStream.close(). I haven't find a solution yet. Hope anyone can help.
public static string viaRijndael(byte[] input, string key, string iV)
{
Rijndael RijCrypt = Rijndael.Create();
RijCrypt.Key = System.Text.Encoding.UTF8.GetBytes(Tools.GetMD5Hash(Tools.GetMD5Hash(key)));
RijCrypt.IV = System.Text.Encoding.UTF8.GetBytes(Tools.GetMD5Hash(Tools.GetMD5Hash(key)).Substring(0, 16));
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, RijCrypt.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(input, 0, input.Length);
cs.Close(); // System.Security.Cryptography.CryptographicException
byte[] DecryptedBytes = ms.ToArray();
return System.Text.Encoding.UTF8.GetString(DecryptedBytes);
}

MSDN Stream.Close documentation says:
"This method calls Dispose, specifying true to release all resources. You do not have to specifically call the Close method. Instead, ensure that every Stream object is properly disposed. You can declare Stream objects within a using block (or Using block in Visual Basic) to ensure that the stream and all of its resources are disposed, or you can explicitly call the Dispose method."
As such I would suggest trying something like the following to handle the disposal of your streams:
public static string viaRijndael(byte[] input, string key, string iV)
{
byte[] decryptedBytes;
using (Rijndael rijCrypt = Rijndael.Create())
{
rijCrypt.Key = System.Text.Encoding.UTF8.GetBytes(Tools.GetMD5Hash(Tools.GetMD5Hash(key)));
rijCrypt.IV = System.Text.Encoding.UTF8.GetBytes(Tools.GetMD5Hash(Tools.GetMD5Hash(key)).Substring(0, 16));
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, rijCrypt.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(input, 0, input.Length);
}
decrpytedBytes = ms.ToArray();
}
}
return System.Text.Encoding.UTF8.GetString(decryptedBytes);
}
All of this and more is explained in good detail on MSDN for the CryptoStream class.

Related

How to encrypt / decrypt binary data in .NET 6.0 with AES in C#?

I'm trying to encrypt binary data with AES in .Net 6.0. Unfortunately, the decryption does not bring back the original data:
public static byte[] Encrypt(byte[] plainBytes)
{
using (var aes = System.Security.Cryptography.Aes.Create())
{
byte[] cipherBytes;
using (MemoryStream cipherStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write))
{
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
cryptoStream.FlushFinalBlock();
cipherStream.Seek(0, SeekOrigin.Begin);
cipherBytes = new byte[cipherStream.Length];
cipherStream.Read(cipherBytes, 0, cipherBytes.Length);
}
byte[] wrongPlainBytes;
using (MemoryStream cipherStream = new MemoryStream(cipherBytes))
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read))
{
wrongPlainBytes = cipherStream.ToArray();
}
bool shouldBeTrue = plainBytes.Equals(wrongPlainBytes);
return cipherBytes;
}
}
After executing this code, shouldBeTrue should be true, but it's false. Also, plainBytes.Length is different from wrongPlainBytes.Length.
What do I wrong while encryption / decryption?
public static byte[] Encrypt( byte[] plainBytes )
{
using ( var aes = System.Security.Cryptography.Aes.Create() )
{
byte[] cipherBytes;
using ( MemoryStream cipherStream = new MemoryStream() )
using ( CryptoStream cryptoStream = new CryptoStream( cipherStream, aes.CreateEncryptor( aes.Key, aes.IV ), CryptoStreamMode.Write ) )
{
cryptoStream.Write( plainBytes, 0, plainBytes.Length );
cryptoStream.FlushFinalBlock();
cipherBytes = cipherStream.ToArray();
}
byte[] wrongPlainBytes;
using ( MemoryStream cipherStream = new MemoryStream() )
using ( CryptoStream cryptoStream = new CryptoStream( cipherStream, aes.CreateDecryptor( aes.Key, aes.IV ), CryptoStreamMode.Write ) )
{
cryptoStream.Write( cipherBytes, 0, cipherBytes.Length );
cryptoStream.FlushFinalBlock();
wrongPlainBytes = cipherStream.ToArray();
}
bool shouldBeTrue = plainBytes.SequenceEqual( wrongPlainBytes );
return cipherBytes;
}
}
Try to avoid direct Read on streams where possible - this operation is not guaranteed to read the amount of bytes you requested, so when you do use it - you need to check the return value (which you ignore) to figure out actual number of bytes that were read, and then act accordingly (do more reads if that amount is less than what you need).
There are several issues in your code, which you can avoid this way:
public static byte[] Encrypt(byte[] plainBytes)
{
using (var aes = System.Security.Cryptography.Aes.Create())
{
byte[] cipherBytes;
using (MemoryStream cipherStream = new MemoryStream()) {
// use leaveOpen parameter here, that way it will NOT dispose cipherStream when closing cryptoStream
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write, true)) {
// just write
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
}
// now, cryptoStream is disposed, so we can be sure all data is propertly written to the cipherStream
// no need to flush or anything
// do not manage buffer yourself, use ToArray
cipherBytes = cipherStream.ToArray();
}
byte[] wrongPlainBytes;
using (MemoryStream cipherStream = new MemoryStream(cipherBytes)) {
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read)) {
using (var output = new MemoryStream()) {
// same story here, avoid direct reads, use CopyTo, it will copy all data, decrypted, to memory stream
cryptoStream.CopyTo(output);
// again, just ToArray to avoid manage buffer directly
wrongPlainBytes = output.ToArray();
}
}
}
bool shouldBeTrue = plainBytes.SequenceEqual(wrongPlainBytes);
return cipherBytes;
}
}

CA2202: Object can be disposed of more than once

I'm getting the following warning in the following code snippet but I cannot understand why
warning CA2202: Microsoft.Usage : Object 'memStream' can be disposed more than once in method 'Encrypt(string)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.
Code:
string Encrypt(string toEncrypt)
{
byte[] key = ...
byte[] iv = ...
using (AesCng aes = new AesCng())
using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
using (MemoryStream memStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
{
UTF7Encoding encoder = new UTF7Encoding();
byte[] bytes = encoder.GetBytes(toEncrypt);
cryptoStream.Write(bytes, 0, bytes.Length);
cryptoStream.FlushFinalBlock();
return Convert.ToBase64String(memStream.ToArray());
}
}
The CryptoStream object, to the best of my knowledge, does not dispose of the passed in Stream when it itself is disposed. So how is it possible that the variable memStream can be disposed of more than once?
Many thanks.
CryptoStream.Dispose() will, by default, dispose the underlying stream. If you don't want that behavior you need to use the constructor overload that explicitly makes the underlying stream remain open when the CryptoStream is disposed.
You can see how that's implemented here.
You can use overloaded CryptoStream constructor with leaveOpen parameter.
And no need this because CryptoStream object in using block
cryptoStream.FlushFinalBlock();
Code:
string Encrypt(string toEncrypt)
{
byte[] key = ...
byte[] iv = ...
using (AesCng aes = new AesCng())
using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
using (MemoryStream memStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write,true))
{
UTF7Encoding encoder = new UTF7Encoding();
byte[] bytes = encoder.GetBytes(toEncrypt);
cryptoStream.Write(bytes, 0, bytes.Length);
return Convert.ToBase64String(memStream.ToArray());
}
}
leaveOpen: true to not close the underlying stream when the CryptoStream object is disposed
CryptoStream ctor

RijndaelManaged doesn't encrypt

I'm trying to encrypt a buffer with RijndaelManaged class without any success. It always return byte[0]. Here's the code:
public byte[] Encrypt(byte[] data, byte[] key)
{
using (var ms = new MemoryStream())
{
using (var aes = RijndaelManaged.Create())
{
aes.Key = _checksumProvider.CalculateChecksum(key);
aes.IV = _checksumProvider.CalculateChecksum(key);
var stream = new CryptoStream(ms, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write);
stream.Write(data, 0, data.Length);
return ms.ToArray();
}
}
}
Key and IV are correctly assigned. Any idea what's wrong with the code? Thanks.
You need to call stream.FlushFinalBlock().
This will perform any final steps in the encryption, and flush the CryptoStream's internal buffer into the underlying memory stream.

The input data is not a complete block

Scenario: One symmetric key, each user has his own IV, the documents are stored in a NVARCHAR(MAX) field. When I try to to decrypt the file, I get:
The input data is not a complete block.
// Create symmetric key
public static byte[] CreateKey()
{
AesCryptoServiceProvider aesCrypto = (AesCryptoServiceProvider)AesCryptoServiceProvider.Create();
byte[] key = aesCrypto.Key;
return key;
}
//Get key (stored in a database)
public static Byte[] GetAppKey()
{
return db.Encryptors.Where(x => x.EncryptorID == 1).Single().EncryptionKey.ToArray();
}
// Get application IV (stored in database)
public static Byte[] GetAppIV()
{
return db.Encryptors.Where(x => x.EncryptorID == 1).Single().IV.ToArray();
}
// Encrypt document (this will be stored in a VARBINARY(MAX) field
public static byte[] EncryptBinaryToBytes(Binary document, byte[] iv)
{
byte[] key = GetAppKey();
byte[] encrypted;
using (AesCryptoServiceProvider aesCsp = new AesCryptoServiceProvider())
{
aesCsp.Key = key;
aesCsp.IV = iv;
ICryptoTransform encryptor = aesCsp.CreateEncryptor(aesCsp.Key, aesCsp.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(document);
}
encrypted = msEncrypt.ToArray();
}
}
}
// return the encrypted document
return encrypted;
}
// Decrypt document
public static byte[] DecryptBytesToBytes(byte[] document, byte[] iv)
{
byte[] key = GetAppKey();
using (AesCryptoServiceProvider aesCsp = new AesCryptoServiceProvider())
{
aesCsp.Key = key;
aesCsp.IV = iv;
ICryptoTransform decryptor = aesCsp.CreateDecryptor(aesCsp.Key, aesCsp.IV);
using (MemoryStream msDecrypt = new MemoryStream())
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
{
using (StreamWriter swDecrypt = new StreamWriter(csDecrypt))
{
swDecrypt.Write(document);
}
byte[] decrypted = msDecrypt.ToArray();
// return the unencrypted document
return decrypted;
}
}
}
}
Thanks in advance.
To store the document
byte[] fileByte = fluUploadFile.FileBytes;
Binary document = new Binary(fileByte);
byte[] appIv = AES.GetAppIV();
byte[] encryptedDocument = AES.EncryptBinaryToBytes(document, appIv);
byte[] decryptedDocument = AES.DecryptBytesToBytes(encryptedDocument, appIv);
Document d = new Document()
{
OriginalName = originalName,
DocSize = fileSize,
BinaryDocument = encryptedDocument,
UploadedName = uploadedFileName,
MimeType = MIMEType,
DocExtension = extension
};
db.Documents.InsertOnSubmit(d);
db.SubmitChanges();
It's really important that you change the data type of the database field to VARBINARY(MAX), that way you avoid issues with character encodings and byte combinations that cannot be interpreted as legal characters.
Also, I think the problem is that you are not closing the streams before calling ToArray() method on the MemoryStream in both encrypt and decrypt routines. It's very important to call Close() in the CryptoStream so that FlushFinalBlock() is called and the encryption process writes the final block to the stream.
Try moving the call to MemoryStream.ToArray() to the outer using block, that is, outside the using block of CryptoStream, so that Dispose() is called on the CryptoStream and call MemoryStream.Close() before that.
Another problem with your code is that you are wrapping the CryptoStream with a StreamWriter, which writes the text representation of the object you pass into the Write method. You should instead write directly to the CryptoStream to avoid any byte to string conversions.

How should my code deal with intentional use of the wrong crypto key?

I am using the following:
RijndaelManaged rm = new RijndaelManaged();
encryptor = rm.CreateEncryptor(key, vector);
decryptor = rm.CreateDecryptor(key, vector);
rm.Padding = PaddingMode.None;
This works good for me. However when I try to intentionally use an incorrect key I get an exception when decoding here:
public byte[] Decrypt(byte[] buffer) {
MemoryStream decryptStream = new MemoryStream();
using (CryptoStream cs = new CryptoStream(decryptStream, decryptor, CryptoStreamMode.Write)) {
cs.Write(buffer, 0, buffer.Length);
}
return decryptStream.ToArray();
}
System.Security.Cryptography.CryptographicException was unhandled by user code
Message=Padding is invalid and cannot be removed.
Is there some way I can cleanly manage this and is this to be expected if I on purpose use the WRONG key?
Now I changed this to the following based on a suggestion below:
public byte[] Decrypt(byte[] buffer)
{
try {
MemoryStream decryptStream = new MemoryStream();
using (CryptoStream cs = new CryptoStream(decryptStream, decryptor, CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
}
return decryptStream.ToArray();
} catch(CryptographicException e){
//... do something with it ...
return null;
}
}
How can I pass up the exception to the next method which is:
public string DecryptFromUrl(string encrypted)
{
return Decrypt(HttpUtility.UrlDecode(encrypted));
}
In any case you should be try { ... } catching() the error and handling it from there. You could return a specific error, generic error, log it etc depending on your requirements.
try
{
... code ...
}
catch(CryptographicException e)
{
... do something with it ...
}
It's ok for the Decrypt method to throw an exception when it can't decrypt - that's what the exceptions are used for. However, it would be better to throw some custom exception insted of the System.Security.Cryptography.CryptographicException which is specific to your current implementation. For example, you may change the Decrypt method to do this:
public byte[] Decrypt(byte[] buffer)
{
MemoryStream decryptStream = new MemoryStream();
try
{
using (CryptoStream cs = new CryptoStream(decryptStream, decryptor, CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
}
}
catch ( CryptographicException ex )
{
throw new InvalidKeyException(ex);
}
return decryptStream.ToArray();
}
Where InvalidKeyException is a class that derives from System.Exception. Now, assuming the decrypt method is part of a class called EncryptionProvider, you can call it like this:
EncryptionProvider encryptionProvider;
// initialize the encryptionProvider with the key
try
{
byte[] decryptedData = encryptionProvider.Decrypt( encryptedData );
// use decryptedData here - it's decrypted with the correct key
}
catch ( InvalidKeyException )
{
// Handle the "wrong key" case here. You may notify the user that they are
// not authorized to access this information or something similar,
// depending on your application logic
}

Categories