I've currently got a folder full of 1280x720 AES encrypted bitmaps.
I'm trying to create a player to loop through the folder decrypt the images and show them in a image box (at reasonable speed)
Importantly I don't want the files to be decrypted to the drive then played. I want to decrypt them in memory only.
Currently the decryption takes about 100ms per image (frame). But I would like to try and get this down to about 10ms if possible.
The above was profiled on a 3.0ghz iCore7
Currently I'm running everything on the UI thread . I thought maybe if I multi-threaded the decrypting I could probably get the speed I wanted, then I will have to store a lot in memory. But I would rather see if I could just make the actual decryption faster.
Here is the decryption function:
private byte[] DecryptFile(string inputFile, string skey)
{
MemoryStream output1 = new MemoryStream();
// ok for tests..
Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(skey, new byte[] { 10, 10, 10, 10, 10, 10, 10, 10 });
try
{
using (RijndaelManaged aes = new RijndaelManaged())
{
byte[] key = k2.GetBytes(16);
/* This is for demostrating purposes only.
* Ideally yu will want the IV key to be different from your key and you should always generate a new one for each encryption in other to achieve maximum security*/
byte[] IV = k2.GetBytes(16);
byte[] cript = File.ReadAllBytes(inputFile);
using (MemoryStream fsCrypt = new MemoryStream(cript))
{
using (ICryptoTransform decryptor = aes.CreateDecryptor(key, IV))
{
using (CryptoStream cs = new CryptoStream(fsCrypt, decryptor, CryptoStreamMode.Read))
{
cs.CopyTo(output1);
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return output1.ToArray() ;
}
Is there a more efficient way to decrypt than the above function?
You can use the AesCryptoServiceProvider which is faster.
In contrast to RijndaelManaged which is a pure managed implementation, AesCryptoServiceProvider uses the Windows Crypto API.
Rfc2898DeriveBytes is designed to be intentionally slow, it used to slow down brute force hacking attempts. Since every file looks like it is using the same key and IV (btw, to address the IV issue you say in the comments, just store the IV has the first bytes in the file itself. The IV does not need to be kept secret, only the key does)
Here is a updated version with a few other tweaks too.
private IEnumerable<byte[]> DecryptFiles(IEnumerable<string> inputFiles, string skey)
{
//Only performing the key calculation once.
Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(skey, new byte[] { 10, 10, 10, 10, 10, 10, 10, 10 });
byte[] key = k2.GetBytes(16)
foreach(var inputFile in inputFiles)
{
yield return DecryptFile(inputFile, key);
}
}
private byte[] DecryptFile(string inputFile, byte[] key)
{
var output1 = new MemoryStream();
try
{
//If you are going to use AES, then use AES, also the CSP is faster than the managed version.
using (var aes = new AesCryptoServiceProvider ())
{
//No need to copy the file in to memory first, just read it from the hard drive.
using(var fsCrypt = File.OpenRead(inputFile))
{
//Gets the IV from the header of the file, you will need to modify your Encrypt process to write it.
byte[] IV = GetIV(fsCrypt);
//You can chain consecutive using statements like this without brackets.
using (var decryptor = aes.CreateDecryptor(key, IV))
using (var cs = new CryptoStream(fsCrypt, decryptor, CryptoStreamMode.Read))
{
cs.CopyTo(output1);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return output1.ToArray();
}
//This function assumes you wrote a 32 bit length then the array that was the read length long.
private static byte[] GetIV(Stream fileStream)
{
var reader = new BinaryReader(fileStream);
var keyLength = reader.ReadInt32();
return reader.ReadBytes(keyLength);
}
If you want to "roll your own", you might want to take a look at this paper describing new instructions on the i7 that are geared towards optimal AES performance. If your C# library doesn't take advantage of those you might get a nice speed boost.
From this paper, it seems that the best you can hope to achieve with a 256 byte key is about 0.32 cycles / byte - for a 1MB file and a 3 GHz processor using all cores with hyperthreading, that would be roughly 0.1 ms per file. That's 1000x faster than you are seeing - so yes, you seem to be well off the "top speed" mark. Same paper claims about 6x slower for single core - still much faster than you are seeing.
I would go looking for another library before writing my own, though. Take a look at the one that Intel provides: http://software.intel.com/en-us/articles/download-the-intel-aesni-sample-library
This is what I use in my system...not sure if it will be faster or not. It just seems like you're doing an un-necessary amount of reading and copying:
Public Shared Function ReadImageAES(ByVal FileName As String) As Image
Dim img As Image = Nothing
Try
Using fs As New FileStream(FileName, FileMode.Open)
Using cs As New CryptoStream(fs, Crypto.AES.CreateDecryptor, CryptoStreamMode.Read)
img = Image.FromStream(cs)
End Using
End Using
Catch ex As Exception
img = Nothing
'Debug.Print("ReadImageAES()failed: " & ex.ToString)
End Try
Return img
End Function
Public Shared Sub WriteImageAES(ByVal FileName As String, ByVal img As Image, Optional ByVal NewUserKey As String = Nothing)
If Not IsNothing(img) Then
Try
If File.Exists(FileName) Then
File.Delete(FileName)
End If
Using fs As New FileStream(FileName, FileMode.OpenOrCreate)
Dim Key() As Byte
If IsNothing(NewUserKey) Then
Key = Crypto.AES.Key
Else
Key = Crypto.SHA256Hash(NewUserKey)
End If
Using cs As New CryptoStream(fs, Crypto.AES.CreateEncryptor(Key, Crypto.AES.IV), CryptoStreamMode.Write)
Dim bmp As New Bitmap(img)
bmp.Save(cs, System.Drawing.Imaging.ImageFormat.Jpeg)
bmp.Dispose()
cs.FlushFinalBlock()
End Using
End Using
Catch ex As Exception
'Debug.Print("WriteImageAES() Failed: " & ex.ToString)
End Try
Else
MessageBox.Show(FileName, "GetImage() Failed")
End If
End Sub
Here's my Crypto class, which was originally written to only target .Net 2.0:
Imports System.IO
Imports System.Text
Imports System.Security.Cryptography
Public Class Crypto
Private Shared _UserKey As String = ""
Private Shared _SHA256 As New SHA256Managed
Private Shared _AES As New RijndaelManaged
Private Const _IV As String = "P5acZMXujMRdmYvFXYfncS7XhrsPNfHkerTnWVT6JcfcfHFDwa" ' <--- this can be anything you want
Public Shared Property UserKey() As String
Get
Return Crypto._UserKey
End Get
Set(ByVal value As String)
Crypto._UserKey = value
Crypto._AES.KeySize = 256
Crypto._AES.BlockSize = 256
Crypto._AES.Key = Crypto.SHA256Hash(Crypto._UserKey)
Crypto._AES.IV = Crypto.SHA256Hash(Crypto._IV)
Crypto._AES.Mode = CipherMode.CBC
End Set
End Property
Public Shared Function SHA256Hash(ByVal value As String) As Byte()
Return Crypto._SHA256.ComputeHash(ASCIIEncoding.ASCII.GetBytes(value))
End Function
Public Shared ReadOnly Property AES() As RijndaelManaged
Get
Return Crypto._AES
End Get
End Property
End Class
I got it almost two time faster when using byte array strictly as a output instead of yours MemoryStream.
My code sample:
byte[] ds = new byte[data.Length];
byte[] decryptedData;
using (Aes aes = CreateAes(key, iv, cipherMode, paddingMode))
{
using (ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
{
int i = 0;
using (MemoryStream msDecrypt = new MemoryStream(data))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
int k;
while ((k = csDecrypt.ReadByte()) != -1)
{
ds[i++] = (byte)k;
}
}
}
decryptedData = new byte[i];
Buffer.BlockCopy(ds, 0, decryptedData, 0, i);
}
}
return decryptedData;
Related
I have written a process where a file is encrypted and uploaded to Azure, then the download process has to be decrypted which is what fails with a "Padding is invalid and cannot be removed" error, or a "Length of the data to decrypt is invalid." error.
I've tried numerous solutions online, including C# Decrypting mp3 file using RijndaelManaged and CryptoStream, but none of them seem to work and I end up just bouncing back and forth between these two errors. The encryption process uses the same key/IV pair that decryption uses, and since it will decrypt a portion of the stream I feel like that's working fine - it just ends up dying with the above errors.
Here is my code, any ideas? Please note that the three variants (cryptoStream.CopyTo(decryptedStream), do {} and while) aren't run together - they are here to show the options I've already tried, all of which fail.
byte[] encryptedBytes = null;
using (var encryptedStream = new MemoryStream())
{
//download from Azure
cloudBlockBlob.DownloadToStream(encryptedStream);
//reset positioning for reading it back out
encryptedStream.Position = 0;
encryptedBytes = encryptedStream.ConvertToByteArray();
}
//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
//stream where decrypted contents will be stored
using (var decryptedStream = new MemoryStream())
{
using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
{
using (var decryptor = aes.CreateDecryptor())
{
//decrypt stream and write it to parent stream
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
//fails here with "Length of the data to decrypt is invalid." error
cryptoStream.CopyTo(decryptedStream);
int data;
//fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
//implying it is in fact decrypting part of it, just not everything
do
{
data = cryptoStream.ReadByte();
decryptedStream.WriteByte((byte)cryptoStream.ReadByte());
} while (!cryptoStream.HasFlushedFinalBlock);
//fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
//implying it is in fact decrypting part of it, just not everything
while ((data = cryptoStream.ReadByte()) != -1)
{
decryptedStream.WriteByte((byte)data);
}
}
}
}
//reset position in prep for reading
decryptedStream.Position = 0;
return decryptedStream.ConvertToByteArray();
}
}
One of the comments mentioned wanting to know what ConvertToByteArray is, and it's just a simple extension method:
/// <summary>
/// Converts a Stream into a byte array.
/// </summary>
/// <param name="stream">The stream to convert.</param>
/// <returns>A byte[] array representing the current stream.</returns>
public static byte[] ConvertToByteArray(this Stream stream)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
The code never reaches this though - it dies before I can ever get it to this point.
After a lot of back and forth from various blogs, I found I actually had a couple of errors in the above code that were nailing me. First, the encryption process was incorrectly writing the array - it was wrapped with a CryptoStream instance, but wasn't actually utilizing that so I was writing the unencrypted data to Azure. Here is the proper route to go with this (fileKey is part of a custom class I created to generate Key/IV pairs, so wherever that is referenced can be changed to the built-in process from RijndaelManaged or anything else you'd utilize for coming up with a key/IV pair):
using (var aes = new RijndaelManaged { KeySize = 256, Key = fileKey.Key, IV = fileKey.IV })
{
using (var encryptedStream = new MemoryStream())
{
using (ICryptoTransform encryptor = aes.CreateEncryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write))
{
using (var originalByteStream = new MemoryStream(file.File.Data))
{
int data;
while ((data = originalByteStream.ReadByte()) != -1)
cryptoStream.WriteByte((byte)data);
}
}
}
var encryptedBytes = encryptedStream.ToArray();
return encryptedBytes;
}
}
Second, since my encryption process involves multiple steps (three total keys per file - container, filename and file itself), when I tried to decrypt, I was using the wrong key (which is seen above when I referenced blobKey to decrypt, which was actually the key used for encrypting the filename and not the file itself. The proper decryption method was:
//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
//stream where decrypted contents will be stored
using (var decryptedStream = new MemoryStream())
{
using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
{
using (var decryptor = aes.CreateDecryptor())
{
//decrypt stream and write it to parent stream
using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
{
int data;
while ((data = cryptoStream.ReadByte()) != -1)
decryptedStream.WriteByte((byte)data);
}
}
}
//reset position in prep for reading
decryptedStream.Position = 0;
return decryptedStream.ConvertToByteArray();
}
}
I had looked into the Azure Encryption Extensions (http://www.stefangordon.com/introducing-azure-encryption-extensions/), but it was a little more local file-centric than I was interested - everything on my end is streams/in-memory only, and retrofitting that utility was going to be more work than it was worth.
Hopefully this helps anyone looking to encrypt Azure blobs with zero reliance on the underlying file system!
Bit late to the party, but in case this is useful to someone who finds this thread:
The following works well for me.
internal static byte[] AesEncryptor(byte[] key, byte[] iv, byte[] payload)
{
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var encryptor = aesAlg.CreateEncryptor(key, iv);
var encrypted = encryptor.TransformFinalBlock(payload, 0, payload.Length);
return iv.Concat(encrypted).ToArray();
}
}
and to decrypt:
internal static byte[] AesDecryptor(byte[] key, byte[] iv, byte[] payload)
{
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
return decryptor.TransformFinalBlock(payload, 0, payload.Length);
}
}
this works for encrypting/decrypting both fixed length hex strings when decoded from hex to byte[] as well as utf8 variable length strings when decoded using Encoding.UTF8.GetBytes().
I have an application in C# that encrypt my files with AES algorithm with this method:
// strKey = "sample-16chr-key"
private static void encryptFile(string inputFile, string outputFile, string strKey)
{
try
{
using (RijndaelManaged aes = new RijndaelManaged())
{
byte[] key = Encoding.UTF8.GetBytes(strKey);
byte[] IV = Encoding.UTF8.GetBytes(strKey);
using (FileStream fsCrypt = new FileStream(outputFile, FileMode.Create))
{
using (ICryptoTransform encryptor = aes.CreateEncryptor(key, IV))
{
using (CryptoStream cs = new CryptoStream(fsCrypt, encryptor, CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
int data;
while ((data = fsIn.ReadByte()) != -1)
{
cs.WriteByte((byte)data);
}
}
}
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
The file is encrypted without an issue.
Then I want to decrypt the encrypted file with my Android (2.2) application. So I do this:
// myDoc is my Document object;
byte[] docBytes = serialize(myDoc);
byte[] key = ("sample-16chr-key").getBytes("UTF-8");
IvParameterSpec iv = new IvParameterSpec(key);
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.DECRYPT_MODE, k, iv);
// IllegalBlockSizeException Occurred
byte[] decryptedDocBytes = c.doFinal(docBytes);
Document decryptedDoc = (Document)deserialize(decryptedDocBytes);
And my serialize/deserialize methods:
private static byte[] serialize(Document obj) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(obj);
return out.toByteArray();
}
private static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is = new ObjectInputStream(in);
return is.readObject();
}
What is the problem here? Both encodings are UTF-8 and the key bytes are the same.
Am I missing something?
If this is not the solution for my application, what am I supposed to do?
The javadoc for IllegalBlockSizeException is pretty clear:
This exception is thrown when the length of data provided to a block cipher is incorrect, i.e., does not match the block size of the cipher.
The problem is that the C# code uses AES in CBC mode with PKCS#7 padding while the Java code uses AES in CBC mode with no padding. You should always spell out your intentions explicitly as opposed to relying on implementation dependent defaults to avoid confusion.
As the Java code uses no padding, the cipher expects a ciphertext with a length that is a multiple of the block size.
The fix would be to change the relevant line to
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
And similarly for the C# code for clarity.
Note that using a static IV defeats several important security aspects of CBC mode. The IV should be unpredictable and unique, preferably from a secure random number generator, and it should be different every time the encryption method is called.
There's also no reason to limit the key to ASCII characters. Doing so makes brute forcing a lot easier.
okay so I have this code for decrypting files
public static byte[] DecryptFile(string inputFile, string skey)
{
RijndaelManaged aes = new RijndaelManaged();
byte[] key = ASCIIEncoding.UTF8.GetBytes(skey);
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
using (CryptoStream cs =
new CryptoStream(fsCrypt, aes.CreateDecryptor(key, key),
CryptoStreamMode.Read))
{
using (BinaryReader reader = new BinaryReader(cs))
{
byte[] str = reader.ReadBytes(Convert.ToInt32(cs.Length));
reader.Close();
cs.Close();
return (str);
}
}
}
}
}
NOW i've got a problem with it, i can't determine the byte length! I tried
cs.Length
but it says the Stream doesn't support seeking (something like tht)
I also tried counting the bytes of the file by
File.ReadAllBytes(encrypted_file_path).Length
but it says the file is in use...it is indeed in use because of the FileStream fsCrypt
for the meantime I replaced cs.Length with some large integer to make it work..like 1000000..the maximum integer that doesn't cause any exception..it does work that way.
You cannot know the length until after you decrypt the entire file.
Therefore, you need to start with a small array, and make it bigger as it gets full.
The MemoryStream class does just that; you can just cs.CopyTo() into a new MemoryStream and call ToArray().
I am facing with problem when decrypting data with usage of TripleDESCryptoServiceProvider. The problem is that decrypted value contains beside of original value some additional, strange characters at the end
Per instance if I provide "rastko" to be encrypted, I will get later with decryption something like this "rastko⥊㮶". For other values it could be different number of 'dummy' characters or in some cases I will get exact value.
Then, I saw that for all encrypted data byte array size is divisible by 8. It looks like any provided data is rounded on value that is divisible by 8. Only in case when original encoded value is divisible by 8, decryption will retrieve appropriate value.
Here are methods that I am using :
public static byte[] EncryptPassword(string password, out byte[] cryptoKey, out byte[] cryptoIV)
{
try
{
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
byte[] unicodePassword = unicodeEncoding.GetBytes(password);
byte[] encryptedPassword;
using (TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider())
{
tripleDes.Key = GetCryptoKey();
tripleDes.Mode = CipherMode.CBC;
tripleDes.Padding = PaddingMode.PKCS7;
cryptoKey = tripleDes.Key;
cryptoIV = tripleDes.IV;
using (MemoryStream memoryStream = new MemoryStream())
{
ICryptoTransform cryptoTransform = tripleDes.CreateEncryptor();
using (
CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(unicodePassword, 0, unicodePassword.Length);
////cryptoStream.FlushFinalBlock();
}
encryptedPassword = memoryStream.ToArray();
}
}
return encryptedPassword;
}
catch (Exception ex)
{
throw new Exception("Password encryption failed !", ex);
}
}
public static string DecryptPassword(byte[] encryptedPassword, byte[] cryptoKey, byte[] cryptoIV)
{
try
{
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
string readablePassword;
using (TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider())
{
tripleDes.Key = cryptoKey;
tripleDes.IV = cryptoIV;
tripleDes.Mode = CipherMode.CBC;
tripleDes.Padding = PaddingMode.PKCS7;
// Create a new MemoryStream using the passed
// array of encrypted data.
using (MemoryStream memoryStream = new MemoryStream(encryptedPassword))
{
// Create crypto transform that defines the basic operations of cryptographic transformations.
ICryptoTransform cryptoTransform = tripleDes.CreateDecryptor();
// Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
decryptoStream.Write(encryptedPassword, 0, encryptedPassword.Length);
///decryptoStream.FlushFinalBlock();
}
byte[] decryptedPassword = memoryStream.ToArray();
//Convert the buffer into a string and return it.
readablePassword = unicodeEncoding.GetString(decryptedPassword, 0, decryptedPassword.Length);
}
}
return readablePassword;
}
catch (Exception ex)
{
throw new Exception("Password decryption failed !", ex);
}
}
private static byte[] GetCryptoKey()
{
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
string plainKey = "rastkoisajev2310982josipasenera153";
byte[] encodedKey = unicodeEncoding.GetBytes(plainKey);
// Prepares 192 bit key
byte[] preparedKey = new byte[24];
Array.Copy(encodedKey, preparedKey, 24);
return preparedKey;
}
Here is sample test invocation :
private static void CryptoTest()
{
string password = "rastko";
byte[] cryptoKey;
byte[] cryptoIV;
byte[] encryptedPassword = Crypto.EncryptPassword(password, out cryptoKey, out cryptoIV);
string decryptedPAssword = Crypto.DecryptPassword(encryptedPassword, cryptoKey, cryptoIV);
}
I have not good experience with security. What I see is that IV vector is 8byte size and as I found it is related to BlockSize, that is 8times greater then IV size. TripleDESCryptoServiceProvider for IV vector is using 8byte value. I can not change this.
Could you please tell me what I have to do or did I wrote something wrongly ?
DES is a 64 bit block cypher. Any text that does not divide cleanly into 64 bit (=8 byte) blocks needs to be padded to make up a whole number of blocks. You need to set padding for encryption and decryption. If you have control of both ends then use PKCS#5 padding to encrypt and decrypt. If you only have control over the decryption end, then ask the encrypting end what padding they are using and expect that.
Note that encrypting a password is normally not the way to go. Use PBKDF2 instead. Don't confuse passwords and keys!
Try to make sure that your CryptoStreams get closed or flushed:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream.flushfinalblock.aspx
If you don't then the padding/unpadding will likely not be performed, and you get trash instead.
After detail investigation I have found the solution for my problem.
I have changed a little bit decryption logic.
Instead of this part in DecryptPassword method :
// Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
decryptoStream.Write(encryptedPassword, 0, encryptedPassword.Length);
///decryptoStream.FlushFinalBlock();
}
byte[] decryptedPassword = memoryStream.ToArray();
//Convert the buffer into a string and return it.
readablePassword = unicodeEncoding.GetString(decryptedPassword, 0, decryptedPassword.Length);
}
I am now using the Read logic from CryptoStream and then I am just removing nullable characters. It is like this now :
// Create a CryptoStream using the MemoryStream and the passed key and initialization vector (IV).
using (CryptoStream decryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read))
{
// Create buffer to hold the decrypted data.
byte[] fromEncrypt = new byte[encryptedPassword.Length];
decryptoStream.Read(fromEncrypt, 0, fromEncrypt.Length);
//Convert the buffer into a string and return it.
readablePassword = unicodeEncoding.GetString(fromEncrypt);
readablePassword = readablePassword.Replace("\0", string.Empty);
}
This works perfectly for me ! Thank you all for your time.
I have following two methods.
1st method
//SymmetricEncryting
private byte[] SymmetricEncrypt()
{
try
{
//Get Byte Value
byte[] x= Encoding.Default.GetBytes("Test");
byte [] y;
//Create Symmetric Key Encription
RijndaelManaged rijndaelManaged = new RijndaelManaged();
//GetSymmetricPublicKey
_symmetricPublicKey = rijndaelManaged.Key;
//Get Symmetric Public IV
_symmetricPublicIv = rijndaelManaged.IV;
using (MemoryStream memoryStream = new MemoryStream(x))
{
//Start EncriptionProcess
var cryptoStream = new CryptoStream(memoryStream,
rijndaelManaged.CreateEncryptor
(_symmetricPublicKey,
_symmetricPublicIv),
CryptoStreamMode.Write);
cryptoStream.Write(x, 0, x.Length);
// Complete the encryption process
//cryptoStream.FlushFinalBlock();
y= memoryStream.ToArray();
}
return y;
}
catch (Exception)
{
throw;
}
}
2nd method
private string Decrypt(
byte[] y,
byte[] symmetricPublicKey,
byte[] symmtricPublicIv)
{
try
{
//Create the Key Container
CspParameters cspParameters = new CspParameters();
//Get the AsyPrivate and Public key from the Container
cspParameters.KeyContainerName = "Keys";
var rsaCryptoServiceProvider = new RSACryptoServiceProvider(cspParameters);
//Decrypt and get the Symmetric Public key
var decryptedSymmetricPubk = rsaCryptoServiceProvider.Decrypt(symmetricPublicKey, false);
//Decrypt and get the Symmetric Public IV
var decryptedSymmetricPubIv = rsaCryptoServiceProvider.Decrypt(symmtricPublicIv, false);
//Create RijndaelManaged object to do the Symmtric dycrption
RijndaelManaged rijndaelManaged = new RijndaelManaged();
//Create cryptostream using decrypted symmetric Public Key and IV
ICryptoTransform iCryptoTransform = rijndaelManaged.CreateDecryptor(decryptedSymmetricPubk,
decryptedSymmetricPubIv);
//Create a memory stream
using (MemoryStream memoryStream = new MemoryStream(y))
{
var cryptoStream = new CryptoStream(memoryStream, iCryptoTransform, CryptoStreamMode.Read);
byte[] z= new byte[y.Length];
cryptoStream.Read(z, 0, z.Length);
//cryptoStream.FlushFinalBlock();
//Convert byte array to string
var x= System.Text.Encoding.Default.GetString(z);
return x;
}
}
catch (Exception)
{
throw;
}
As you see in the code i am trying to encrypt a string using symmetric encryption.I encrypt the symmetric public key and Iv by using the asymmetric public key which i have already created.Then i am trying to decrypt the encrypted string .
Problem 1
What is the purpose of having cryptoStream.FlushFinalBlock(); on both encryption and decryption.As i learned from msdn it will end the processes running on the cyptostream
Problem 2
If i uncomment the line cryptoStream.FlushFinalBlock(); it throws an exception
"Memory stream is not expandable.". But if i comment the line it will work fine and return a byte array.
Problem 3
However the second method throws an exception "system.security.cryptography.cryptographicexception length of the data to decrypt is invalid ,on the execution of line cryptoStream.Read(z, 0, z.Length);
I couldn't find the actual cause of these errors on my debugging .Also i did some search on Google.But unfortunately i couldn't find any solution.Can any one please explain the answer?
You are encrypting using PKCS-padding (this is the default). AES/Rijndael is a block-cipher, which means that it only can encrypt blocks of 16 bytes at a time. To allow block-cipher to encrypt data of arbitrary sizes we use a padding algorithm. PKCS-padding works by adding 1-16 bytes at the end when encrypting and removing them when decrypting. The length of the padding is encoded in the padding itself.
You need the FlushFinalBlock when encrypting to let the CryptoStream know that there is no more incoming data and it should add the padding. It is not necessary and should not be used when you are using the CryptoStream in Read-mode.
The first exception come because you are using the plaintext-array as a backing store for the MemoryStream. Because of the padding the encryption will be larger than the plaintext.
The second exception is because you removed the FlushFinalBlock statement and because the MemoryStream is not allowed to resize to make an array of the correct length. The encrypted data should always be a multiple of 16 bytes, but since the MemoryStream will reuse x, y will have the same length as x, which is not always a valid length.
The solution is:
Use FlushFinalBlock in SymmetricEncrypt.
Replace using (MemoryStream memoryStream = new MemoryStream(x)) and using (MemoryStream memoryStream = new MemoryStream(y)) with using (MemoryStream memoryStream = new MemoryStream()). This will allow the MemoryStreams to resize freely.
Oddly, it works for me to just do a write operation when de-crypting as well. Something like:
var decryptMemoryStream = new MemoryStream();
var decryptStream = new CryptoStream(decryptMemoryStream, iCryptoTransform , CryptoStreamMode.Write);
//write the unencrypted data array to the stream
decryptStream.Write(y, 0, y.Length);
decryptStream.Flush();
decryptStream.Close();
var decryptedData = decryptMemoryStream.ToArray();