I have a very big csv file which is encrypted using AES. The code that does the encryption
using var aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = initializationVector;
using var memoryStream = new MemoryStream();
var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.Flush();
This is later saved into a file. On the decryption end, I'm trying to decrypt it in chunks, e.g.
using var sourceStream = File.OpenRead(path_to_encrypted_file);
using var aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = iv;
using (var fs = File.Create(path_to_decrypted_file))
using (var cryptoStream = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Write)
{
var dataBuffer = new byte[81290];
int read;
while ((read = await sourceStream.ReadAsync(dataBuffer)) != 0)
{
ReadOnlyMemory<byte> buffer = dataBuffer.AsMemory().Slice(0, read);
await cryptoStream.WriteAsync(buffer);
await cryptoStream.FlushAsync();
}
}
File is decrypted, however, I see some random bytes and empty lines at the end of the file
Is there anything wrong with how I decrypt ?
There's a couple potential issues I'd investigate first, at least in the existing provided code. There may be more depending on how you're generating the initial data byte array, how you're generating your key, how you're writing the encrypted stream to disk, etc.
You're using ECB and you almost certainly shouldn't. It isn't doing anything with your IV, either. Consider CBC or GCM depending on the application. https://stackoverflow.com/a/22958889/13374279
You're not using a padding mode. Unless your data is exactly contained within the block size, there's a chance you're losing some data, which might be contributing to the gibberish at the end.
You don't show the original encrypting stream disposal, you just show the Flush(). Depending on its disposal, it is likely not calling the CryptoStream's FlushFinalBlock() method, which is important. Given the lack of the padding mode, if you add this in, you'll likely suddenly see yourself with an exception here to alert you that The input data is not a complete block. due to #2 until you swap that out.
Thanks to the answer by #Adam G I reimplemented encrypt/decrypt following suggestions in the answer + comments.
A little background – I needed a solution where encryption happens on the client machine (disconnected from the internet) & decryption later on takes place in the cloud once the encrypted file uploaded to a blob storage.
I wanted to have a hybrid encryption, where key is RSA encrypted, data - AES.
So the file contents on the client:
RSA encrypted key
RSA encrypted IV (RSA encryption of the IV is not necessary AFAIK)
AES encrypted data
This is the final implementation:
// Local
var localRsa = RSA.Create();
localRsa.ImportRSAPublicKey(
Convert.FromBase64String(public_key),
out var _);
var localAes = Aes.Create();
localAes.GenerateKey();
localAes.GenerateIV();
localAes.Mode = CipherMode.CBC;
localAes.Padding = PaddingMode.PKCS7;
using (var dataStream = File.OpenRead(file_to_encrypt))
using (var secretFileStream = File.Create(encrypted_file))
{
await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.Key, RSAEncryptionPadding.OaepSHA256));
await secretFileStream.WriteAsync(localRsa.Encrypt(localAes.IV, RSAEncryptionPadding.OaepSHA256));
using (var cryptoStream = new CryptoStream(secretFileStream, localAes.CreateEncryptor(localAes.Key, localAes.IV), CryptoStreamMode.Write))
{
await dataStream.CopyToAsync(cryptoStream);
}
}
And the decryption piece:
// Cloud
var cloudRsa = RSA.Create();
cloudRsa.ImportRSAPrivateKey(
Convert.FromBase64String(private_key),
out var _);
var cloudAes = Aes.Create();
cloudAes.Mode = CipherMode.CBC;
cloudAes.Padding = PaddingMode.PKCS7;
using (var secretFileStream = File.OpenRead(encrypted_file))
{
var keyBuffer = new byte[256];
await secretFileStream.ReadAsync(keyBuffer, 0, keyBuffer.Length);
cloudAes.Key = cloudRsa.Decrypt(keyBuffer, RSAEncryptionPadding.OaepSHA256);
var ivBuffer = new byte[256];
await secretFileStream.ReadAsync(ivBuffer, 0, keyBuffer.Length);
cloudAes.IV = cloudRsa.Decrypt(ivBuffer, RSAEncryptionPadding.OaepSHA256);
secretFileStream.Position = 512;
using (var plainTextStream = File.Create(decrypted_file))
{
using (var cryptoStream = new CryptoStream(secretFileStream, cloudAes.CreateDecryptor(cloudAes.Key, cloudAes.IV), CryptoStreamMode.Read))
{
await cryptoStream.CopyToAsync(plainTextStream);
}
}
}
I'm trying to encrypt & decrypt some data using AES. But i'm only getting garbled output. What am I doing wrong?
static void Test()
{
byte[] myFileBytes; // Will contain encrypted data. First the IV, then the ciphertext.
var myPassword = "helloworld";
var dataToEncrypt = "this is a test";
// STEP 1: Encrypt some data:
byte[] key;
using (var sha256 = SHA256.Create())
key = sha256.ComputeHash(Encoding.UTF8.GetBytes(myPassword));
using (var myFileStream = new MemoryStream())
using (var aes = System.Security.Cryptography.Aes.Create())
{
aes.Key = key;
myFileStream.Write(aes.IV); // Use the default created by AES, which is presumably non-pseudo random
using (var cryptoStream = new CryptoStream(myFileStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(Encoding.UTF8.GetBytes(dataToEncrypt));
cryptoStream.Flush();
myFileBytes = myFileStream.ToArray(); // We are done!
} // Disposing CryptoStream disposes the underlying MemoryStream
}
// STEP 2: Decrypt it to verify that it works
using (var aes = System.Security.Cryptography.Aes.Create())
{
using (var myFileStream = new MemoryStream(myFileBytes))
{
var iv = new byte[aes.IV.Length];
myFileStream.Read(iv, 0, iv.Length);
using (var cryptoStream = new CryptoStream(myFileStream, aes.CreateEncryptor(key, iv), CryptoStreamMode.Read))
using (var copyStream = new MemoryStream())
{
cryptoStream.CopyTo(copyStream);
var decrypted = Encoding.UTF8.GetString(copyStream.ToArray());
Debug.Assert(dataToEncrypt == decrypted); // Fails!
}
}
}
}
I would take a look at the example in the documentation and compare to your code.
Notably when decrypting you are using aes.CreateEncryptor(key, iv). It should probably be aes.CreateDecryptor(key, iv).
The example from the docs also inputs the key and IV when calling CreateEncryptor, but I'm not sure if that is required or not.
You should probably not use sha256 to generate the key from a password. The correct way would be a key derivation algorithm. For example Rfc2898DeriveBytes
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();
per my question Aes Encryption... missing an important piece, I have now learned that my assumption for creating a reversible encryption on a string was a bit off. I now have
public static byte[] EncryptString(string toEncrypt, byte[] encryptionKey)
{
var toEncryptBytes = Encoding.UTF8.GetBytes(toEncrypt);
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return ms.ToArray();
}
}
}
}
and this produces consistent results; however, I will not be able to decrypt without knowing/ setting the initialization vector. I really do not want to pass three values into this method (on for the IV), which leaves me with hardcoding the IV or deriving it from the key. I'd like to know if this is a good practice, or if it will render the encrypted value vulnerable to attack somehow... or am I really overthinking this and should just hardcode the IV?
UPDATE
Per Iridium's suggestion, I tried something like this instead:
public static byte[] EncryptString(string toEncrypt, byte[] encryptionKey)
{
if (string.IsNullOrEmpty(toEncrypt)) throw new ArgumentException("toEncrypt");
if (encryptionKey == null || encryptionKey.Length == 0) throw new ArgumentException("encryptionKey");
var toEncryptBytes = Encoding.UTF8.GetBytes(toEncrypt);
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
{
using (var ms = new MemoryStream())
{
ms.Write(provider.IV, 0, 16);
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return ms.ToArray();
}
}
}
}
public static string DecryptString(byte[] encryptedString, byte[] encryptionKey)
{
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var ms = new MemoryStream(encryptedString))
{
byte[] buffer;
ms.Read(buffer, 0, 16);
provider.IV = buffer;
using (var decryptor = provider.CreateDecryptor(provider.Key, provider.IV))
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
byte[] decrypted = new byte[encryptedString.Length];
var byteCount = cs.Read(decrypted, 0, encryptedString.Length);
return Encoding.UTF8.GetString(decrypted, 0, byteCount);
}
}
}
}
}
however, this shows something odd in my unit test:
[TestMethod]
public void EncryptionClosedLoopTest()
{
var roundtrip = "This is the data I am encrypting. There are many like it but this is my encryption.";
var encrypted = Encryption.EncryptString(roundtrip, encryptionKey);
var decrypted = Encryption.DecryptString(encrypted, encryptionKey);
Assert.IsTrue(roundtrip == decrypted);
}
my decrypted text shows up as "92ʪ�F"�,hpv0�� I am encrypting. There are many like it but this is my encryption." which seems almost right but of course completely wrong. It looks like I'm close though. Am I missing an offset on the memory stream?
The IV should be random and unique for every run of your encryption method. Deriving it from the key/message or hard-coding it is not sufficiently secure. The IV can be generated within this method, instead of passed into it, and written to the output stream prior to the encrypted data.
When decrypting, the IV can then be read from the input before the encrypted data.
When Encrypting, generate your IV and pre-pend it to the cipher text (something like this)
using (var aes= new AesCryptoServiceProvider()
{
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
var input = Encoding.UTF8.GetBytes(originalPayload);
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
{
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
//Prepend IV to data
//tBinaryWriter.Write(iv); This is the original broken code, it encrypts the iv
cipherStream.Write(iv); //Write iv to the plain stream (not tested though)
tBinaryWriter.Write(input);
tCryptoStream.FlushFinalBlock();
}
string encryptedPayload = Convert.ToBase64String(cipherStream.ToArray());
}
}
When decrypting this back, get first 16 bytes out and use it in crypto stream
var aes= new AesCryptoServiceProvider()
{
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
//get first 16 bytes of IV and use it to decrypt
var iv = new byte[16];
Array.Copy(input, 0, iv, 0, iv.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cs))
{
//Decrypt Cipher Text from Message
binaryWriter.Write(
input,
iv.Length,
input.Length - iv.Length
);
}
return Encoding.Default.GetString(ms.ToArray());
}
Great input from folks. I took the combined answers from ankurpatel and Konstantin and cleaned it up and added some convenient method overrides. This works as of June 2019 in .NET Core 2.2.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
private const int AesKeySize = 16;
public static void Main()
{
// the data to encrypt
var message = "Here is some data to encrypt!";
// create KeySize character key
var key = "g(KMDu(EEw63.*V`";
// encrypt the string to a string
var encrypted = AesEncrypt(message, key);
// decrypt the string to a string.
var decrypted = AesDecrypt(encrypted, key);
// display the original data and the decrypted data
Console.WriteLine($"Original: text: {encrypted}");
Console.WriteLine($"Round Trip: text: {decrypted}");
}
static string AesEncrypt(string data, string key)
{
return AesEncrypt(data, Encoding.UTF8.GetBytes(key));
}
static string AesDecrypt(string data, string key)
{
return AesDecrypt(data, Encoding.UTF8.GetBytes(key));
}
static string AesEncrypt(string data, byte[] key)
{
return Convert.ToBase64String(AesEncrypt(Encoding.UTF8.GetBytes(data), key));
}
static string AesDecrypt(string data, byte[] key)
{
return Encoding.UTF8.GetString(AesDecrypt(Convert.FromBase64String(data), key));
}
static byte[] AesEncrypt(byte[] data, byte[] key)
{
if (data == null || data.Length <= 0)
{
throw new ArgumentNullException($"{nameof(data)} cannot be empty");
}
if (key == null || key.Length != AesKeySize)
{
throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
}
using (var aes = new AesCryptoServiceProvider
{
Key = key,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
{
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
// prepend IV to data
cipherStream.Write(iv);
tBinaryWriter.Write(data);
tCryptoStream.FlushFinalBlock();
}
var cipherBytes = cipherStream.ToArray();
return cipherBytes;
}
}
}
static byte[] AesDecrypt(byte[] data, byte[] key)
{
if (data == null || data.Length <= 0)
{
throw new ArgumentNullException($"{nameof(data)} cannot be empty");
}
if (key == null || key.Length != AesKeySize)
{
throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
}
using (var aes = new AesCryptoServiceProvider
{
Key = key,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
// get first KeySize bytes of IV and use it to decrypt
var iv = new byte[AesKeySize];
Array.Copy(data, 0, iv, 0, iv.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cs))
{
// decrypt cipher text from data, starting just past the IV
binaryWriter.Write(
data,
iv.Length,
data.Length - iv.Length
);
}
var dataBytes = ms.ToArray();
return dataBytes;
}
}
}
I modified your decryption method as follows and it works:
public static string DecryptString(byte[] encryptedString, byte[] encryptionKey)
{
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
using (var ms = new MemoryStream(encryptedString))
{
// Read the first 16 bytes which is the IV.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
using (var decryptor = provider.CreateDecryptor())
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
return sr.ReadToEnd();
}
}
}
}
}
}
The problem with your implementation is that you are reading too many bytes into the CryptoStream. You really need to read encryptedText.Length - 16. Using a StreamReader simplifies this, since you don't need to worry about offsets anywhere anymore.
The accepted answer is correct, but doesn't provide a good example of how to get a random IV.
It turns out that this is a lot easier than people are trying to make it. The AesCryptoServiceProvider in .NET automatically generates a cryptographically random IV every time you construct one. And if you need to use the same instance for multiple encryptions, you can call GenerateIV()
You can also prepend the IV to the encrypted value before returning it and have the decrypting end pull it off
private static void Main(string[] args) {
var rnd = new Random();
var key = new byte[32]; // For this example, I'll use a random 32-byte key.
rnd.NextBytes(key);
var message = "This is a test";
// Looping to encrypt the same thing twice just to show that the IV changes.
for (var i = 0; i < 2; ++i) {
var encrypted = EncryptString(message, key);
Console.WriteLine(encrypted);
Console.WriteLine(DecryptString(encrypted, key));
}
}
public static string EncryptString(string message, byte[] key) {
var aes = new AesCryptoServiceProvider();
var iv = aes.IV;
using (var memStream = new System.IO.MemoryStream()) {
memStream.Write(iv, 0, iv.Length); // Add the IV to the first 16 bytes of the encrypted value
using (var cryptStream = new CryptoStream(memStream, aes.CreateEncryptor(key, aes.IV), CryptoStreamMode.Write)) {
using (var writer = new System.IO.StreamWriter(cryptStream)) {
writer.Write(message);
}
}
var buf = memStream.ToArray();
return Convert.ToBase64String(buf, 0, buf.Length);
}
}
public static string DecryptString(string encryptedValue, byte[] key) {
var bytes = Convert.FromBase64String(encryptedValue);
var aes = new AesCryptoServiceProvider();
using (var memStream = new System.IO.MemoryStream(bytes)) {
var iv = new byte[16];
memStream.Read(iv, 0, 16); // Pull the IV from the first 16 bytes of the encrypted value
using (var cryptStream = new CryptoStream(memStream, aes.CreateDecryptor(key, iv), CryptoStreamMode.Read)) {
using (var reader = new System.IO.StreamReader(cryptStream)) {
return reader.ReadToEnd();
}
}
}
}
[EDIT: I modified my answer to include how to pass the IV in the encrypted value and get it when decrypting. I also refactored the example a bit]
In order to resolve the setting of IV on the provider (As Iridium pointed out):
ms.Read(provider.IV, 0, 16);
I added the following to your code:
var iv = new byte[provider.IV.Length];
memoryStream.Read(iv, 0, provider.IV.Length);
using (var decryptor = provider.CreateDecryptor(key, iv);
granted, my key is not set by the provider on each run. I generated it once and then stored it. The IV is randomly generated off of the provider for each encryption.
In my case, to generate the IV, I use something like this
/// <summary>
/// Derives password bytes
/// </summary>
/// <param name="Password">password</param>
/// <returns>derived bytes</returns>
private Rfc2898DeriveBytes DerivePass(string Password)
{
byte[] hash = CalcHash(Password);
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Password, hash, _KEY_ITER);
return pdb;
}
/// <summary>
/// calculates the hash of the given string
/// </summary>
/// <param name="buffer">string to hash</param>
/// <returns>hash value (byte array)</returns>
private byte[] CalcHash(string buffer)
{
RIPEMD160 hasher = RIPEMD160.Create();
byte[] data = Encoding.UTF8.GetBytes(buffer);
return hasher.ComputeHash(data);
}
that is, I calculate the password hash using RIPEMD160 and use it to generate the derived bytes, at that point, when it comes to intializing the encryption/decryption I just use something like this
Rfc2898DeriveBytes pdb = DerivePass(Password);
SymmetricAlgorithm alg = _engine;
alg.Key = pdb.GetBytes(_keySize);
alg.IV = pdb.GetBytes(_IVSize);
I don't know if it's "correct" (probably crypto gurus here will shoot at me :D), but, at least, it gives me a decent IV and I don't have to store it "somewhere" since just entering the correct password will give back the needed IV value; as a note, the _engine in the above example is declared as "SymmetricAlgorithm" and initialized using something like this
_engine = Rijndael.Create();
_keySize = (_engine.KeySize / 8);
_IVSize = (_engine.BlockSize / 8);
which creates the desired crypto objects and initializes the key and IV sizes
To generate random IV you would need a truly random number. Whichever language specific API you use for generating the random number, should generate true random number. Both android and ios have apis which generate random numbers based on sensor data.
I recently implemented AES 256 with random IV (Generated using really random numbers) and hashed key. For more secure(random IV + hashed key) cross platform (android, ios, c#) implementation of AES see my answer here - https://stackoverflow.com/a/24561148/2480840
I am trying to encrypt a stream (coming from a file) using AesManaged. I can encrypt the file without error, but on decryption I get the following CryptographicException:
Padding is invalid and cannot be
removed.
The exception is raised when the CryptoStream is being disposed. I use the following to encrypt the input data:
public byte[] Encrypt(Stream plain)
{
// Create a decrytor to perform the stream transform.
using( var msEncrypt = new MemoryStream() )
{
using (ICryptoTransform encryptor = _myAes.CreateEncryptor(_myAes.Key, _myAes.IV))
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (BinaryWriter swEncrypt = new BinaryWriter(csEncrypt))
{
int buf_size = 32768;
byte[] buffer = new byte[buf_size];
int read = 0;
while ((read = plain.Read(buffer, 0, buf_size)) > 0)
{
swEncrypt.Write(buffer, 0, read);
}
}
return msEncrypt.ToArray();
}
}
And this to decrypt the data:
public byte[] Decrypt(Stream cipherText)
{
using (MemoryStream ms = new MemoryStream())
{
// Create a decrytor to perform the stream transform.
using (ICryptoTransform decryptor = _myAes.CreateDecryptor(_myAes.Key, _myAes.IV))
using (CryptoStream csDecrypt = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
using (BinaryWriter swDecrypt = new BinaryWriter(csDecrypt))
{
int buf_size = 32768;
byte[] buffer = new byte[buf_size];
int read = 0;
while ((read = cipherText.Read(buffer, 0, buf_size)) > 0)
{
swDecrypt.Write(buffer, 0, read);
}
}
return ms.ToArray();
}
}
Any ideas about why this exception is coming up would be great. Thanks
UPDATE
Here is where the Aes object was created, note the Key and IV are just set to their current values temporarily, it is not the real key that will be used:
private Crypto()
{
_myAes = new AesManaged();
_myAes.Padding = PaddingMode.PKCS7;
_myAes.KeySize = 128;
_myAes.Key = Enumerable.Repeat((byte)'B', 128 / 8).ToArray();
_myAes.IV = Enumerable.Repeat((byte)'C', 128 / 8).ToArray();
}
In the past I got this exception when I tried to decrypt a buffer whose length was not a multiple of 16 bytes.
Did you try calling Flush on the CryptoStream before it is disposed? Possibly, if it isn't flushed then it ends up trying to decrypt a buffer with a non-aligned length.
And another note - I don't know if this will solve your problem, but when you create a CryptoStream in order to decrypt the buffer, shouldn't you be using CryptoStreamMode.Read instead of CryptoStreamMode.Write?
Ensure that you finish the CryptoStream cleanly on the write side. You may need to call FlushFinalBlock() to ensure that the end-of-stream padding is written through -- otherwise, you are likely to end up missing the final block of the stream, which will result in an invalid padding exception.
I don't know if it is still relevant to post something after 4 years but, you should try to set padding to none. I got the same problem with 3DES and the issue got solved with that (but make sure the length of data to be decrypted is correct...)
private Crypto()
{
_myAes = new AesManaged();
_myAes.Padding = PaddingMode.none; //rather than _myAes.Padding = PaddingMode.PKCS7;
_myAes.KeySize = 128;
_myAes.Key = Enumerable.Repeat((byte)'B', 128 / 8).ToArray();
_myAes.IV = Enumerable.Repeat((byte)'C', 128 / 8).ToArray();
}