Related
I'm using a string Encryption/Decryption class similar to the one provided here as a solution.
This worked well for me in .Net 5.
Now I wanted to update my project to .Net 6.
When using .Net 6, the decrypted string does get cut off a certain point depending on the length of the input string.
▶️ To make it easy to debug/reproduce my issue, I created a public repro Repository here.
The encryption code is on purpose in a Standard 2.0 Project.
Referencing this project are both a .Net 6 as well as a .Net 5 Console project.
Both are calling the encryption methods with the exact same input of "12345678901234567890" with the path phrase of "nzv86ri4H2qYHqc&m6rL".
.Net 5 output: "12345678901234567890"
.Net 6 output: "1234567890123456"
The difference in length is 4.
I also looked at the breaking changes for .Net 6, but could not find something which guided me to a solution.
I'm glad for any suggestions regarding my issue, thanks!
Encryption Class
public static class StringCipher
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 128;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate128BitsOfRandomEntropy();
var ivStringBytes = Generate128BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = Aes.Create())
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [16 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 16 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 16 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = Aes.Create())
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
private static byte[] Generate128BitsOfRandomEntropy()
{
var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits.
using (var rngCsp = RandomNumberGenerator.Create())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
Calling code
var input = "12345678901234567890";
var inputLength = input.Length;
var inputBytes = Encoding.UTF8.GetBytes(input);
var encrypted = StringCipher.Encrypt(input, "nzv86ri4H2qYHqc&m6rL");
var output = StringCipher.Decrypt(encrypted, "nzv86ri4H2qYHqc&m6rL");
var outputLength = output.Length;
var outputBytes = Encoding.UTF8.GetBytes(output);
var lengthDiff = inputLength - outputLength;
The reason is this breaking change:
DeflateStream, GZipStream, and CryptoStream diverged from typical
Stream.Read and Stream.ReadAsync behavior in two ways:
They didn't complete the read operation until either the buffer passed
to the read operation was completely filled or the end of the stream
was reached.
And the new behaviour is:
Starting in .NET 6, when Stream.Read or Stream.ReadAsync is called on
one of the affected stream types with a buffer of length N, the
operation completes when:
At least one byte has been read from the stream, or The underlying
stream they wrap returns 0 from a call to its read, indicating no more
data is available.
In your case you are affected because of this code in Decrypt method:
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
You do not check how much bytes Read actually read and whether it read them all. You could get away with this in previous versions of .NET because as mentioned CryptoStream behaviour was different from other streams, and because your buffer length is enough to hold all data. However, this is no longer the case and you need to check it as you would do for other streams. Or even better - just use CopyTo:
using (var plainTextStream = new MemoryStream())
{
cryptoStream.CopyTo(plainTextStream);
var plainTextBytes = plainTextStream.ToArray();
return Encoding.UTF8.GetString(plainTextBytes, 0, plainTextBytes.Length);
}
Or even better as another answer suggests, since you decrypt UTF8 text:
using (var plainTextReader = new StreamReader(cryptoStream))
{
return plainTextReader.ReadToEnd();
}
I think your problem is in here:
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
From the Stream.Read docs:
An implementation is free to return fewer bytes than requested even if the end of the stream has not been reached.
So that single call to Read is not guaranteed to read all available bytes (up to plainTextBytes.Length) -- it's well within its rights to read a smaller number of bytes.
.NET 6 has many performance improvements, and I wouldn't be surprised if this was the sort of trade-off they'd make in the name of performance.
You'll have to be good, and keep calling Read until it returns 0, which indicates that there's no more data to return.
However, it's a lot easier to just use a StreamReader, which will also take care of the UTF-8 decoding for you.
return new StreamReader(cryptoStream).ReadToEnd();
I use these 2 extension methods in my .net6 project.
namespace WebApi.Utilities;
public static class StringUtil
{
static string key = "Mohammad-Komaei#Encrypt!keY#";
public static string Encrypt(this string text)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(text))
throw new ArgumentException("The text must have valid value.", nameof(text));
var buffer = Encoding.UTF8.GetBytes(text);
var hash = SHA512.Create();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, encryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(buffer))
{
plainStream.CopyTo(aesStream);
}
var result = resultStream.ToArray();
var combined = new byte[aes.IV.Length + result.Length];
Array.ConstrainedCopy(aes.IV, 0, combined, 0, aes.IV.Length);
Array.ConstrainedCopy(result, 0, combined, aes.IV.Length, result.Length);
return Convert.ToBase64String(combined);
}
}
}
public static string Decrypt(this string encryptedText)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(encryptedText))
throw new ArgumentException("The encrypted text must have valid value.", nameof(encryptedText));
var combined = Convert.FromBase64String(encryptedText);
var buffer = new byte[combined.Length];
var hash = SHA512.Create();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
var iv = new byte[aes.IV.Length];
var ciphertext = new byte[buffer.Length - iv.Length];
Array.ConstrainedCopy(combined, 0, iv, 0, iv.Length);
Array.ConstrainedCopy(combined, iv.Length, ciphertext, 0, ciphertext.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, decryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(ciphertext))
{
plainStream.CopyTo(aesStream);
}
return Encoding.UTF8.GetString(resultStream.ToArray());
}
}
}
}
After upgrading from .net 2.2 to 6 I was facing exactly the same issue. It does not read entire buffer - mostly reads only upto 16 bytes, so, just break it down in loop upto maximum of 16 bytes.
This code may help:
int totalRead = 0;
int maxRead = 16;
while (totalRead < plainTextBytes.Length)
{
var countLeft = plainTextBytes.Length - totalRead;
var count = countLeft < 16 ? countLeft : maxRead;
int bytesRead = cryptoStream.Read(plainTextBytes, totalRead, count);
totalRead += bytesRead;
if (bytesRead == 0) break;
}
I am attempting to decrypt a file to ONLY the process memory. I do not want the actual file to be sent to plain text as it will be storing sensitive data. I do not want the raw text sitting on the system at all.
I am currently testing with a eula file in C:\ BUT get the same issue no matter what file I use.
I am using AES with salting. Decrypting the file does work as right now I am dumping the decrypted data to the text document but when I am attempting to compile the decrpytedBytes into a string, it only outputs 3 characters that are non-existent in that order anywhere inside of the document.
https://i.imgur.com/WAQ2njB.png
Those 2 characters show up while using System.Text.Encoding.UTF8.GetString(bytesDecrypted, 0, bytesDecrypted.Length) to compile the byte array to a string.
I have attempted just a basic .ToString() but that returned System.Byte[] and nothing more
https://i.imgur.com/Gg5Et72.png
While using var str = System.Text.Encoding.Default.GetString(bytesDecrypted) it only outputs ÿþ*
https://i.imgur.com/94hMuB3.png
Here is the code I am using for encryption and decryption
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
public void EncryptFile(string file, string fileEncrypted, string password)
{
byte[] bytesToBeEncrypted = File.ReadAllBytes(file);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
File.WriteAllBytes(fileEncrypted, bytesEncrypted);
listBox1.Items.Add("Enrypted the file");
}
public void DecryptFile(string fileEncrypted, string file, string password)
{
byte[] bytesToBeDecrypted = File.ReadAllBytes(fileEncrypted);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
listBox1.Items.Add("Attempting Decryption");
File.WriteAllBytes(file, bytesDecrypted);
var str = System.Text.Encoding.Default.GetString(bytesDecrypted);
richTextBox1.Text = str;
}
If you have any idea/clues on how I could manage to get this working I would greatly appreciate it!
You used the incorrect encoding to to decode your decrypted byte array. The encoding of the original text file is most likely Unicode/UTF-16. Thus, use the Encoding.Unicode encoding to decode the decrypted byte array back to text:
var str = System.Text.Encoding.Unicode.GetString(bytesDecrypted);
Some background information
So, what made me think that the encoding of the original text file is UTF-16/Unicode?
This information from the question gives a crucial hint:
While using var str = System.Text.Encoding.Default.GetString(bytesDecrypted) it only outputs ÿþ*
Note the ÿþ. This is how an UTF-16 LE BOM (*) appears if text data having this BOM is decoded/shown using the ISO/IEC 8859-1 (or CP-1252) code page, which often is the default code page used in many (english/non-localized) Windows installations.
(*) The UTF-16 LE BOM (UTF-16 Little-Endian Byte Order Mark) are two bytes 0xFF,0xFE. To learn more about what BOMs are and what their purpose is, i suggest this Wikipedia article: https://en.wikipedia.org/wiki/Byte_order_mark
Found this answer that I think applies to your issue. Pay special attention to "encryptedData = output.ToArray();"
Reading from a cryptostream to the end of the stream
byte[] encryptedData;
rijCrypto.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
rijCrypto.KeySize = 256;
using (var input = new MemoryStream(Encoding.Unicode.GetBytes(tempData)))
using (var output = new MemoryStream())
{
var encryptor = rijCrypto.CreateEncryptor();
using (var cryptStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
{
var buffer = new byte[1024];
var read = input.Read(buffer, 0, buffer.Length);
while (read > 0)
{
cryptStream.Write(buffer, 0, read);
read = input.Read(buffer, 0, buffer.Length);
}
cryptStream.FlushFinalBlock();
encryptedData = output.ToArray();
}
}
I am trying to encrypt a Generic Stream in C#. Although the program has no problems, the encryption and decryption return blank when converted to strings. Any help is appreciated.
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
private void Encrypt(Stream input, Stream output, String password)
{
input.Position = 0;
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
using (var stream = new MemoryStream())
{
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, bytesRead);
var tmp = AES_Encrypt(buffer, passwordBytes);
output.Write(tmp, 0, tmp.Length);
}
}
}
private void Decrypt(Stream input, Stream output, String password)
{
input.Position = 0;
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
using (var stream = new MemoryStream())
{
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, bytesRead);
var tmp = AES_Decrypt(buffer, passwordBytes);
output.Write(tmp, 0, tmp.Length);
}
}
}
static void Main(string[] args)
{
Program obj = new Program();
var message = new MemoryStream();
var cipher = new MemoryStream();
string tmp = "This is a test if the encryption is working!";
StreamWriter sw = new StreamWriter(message);
sw.Write(tmp);
obj.Encrypt(message, cipher, "password");
cipher.Position = 0;
message = new MemoryStream();
obj.Decrypt(cipher, message, "password");
using (var memoryStream = new MemoryStream())
{
message.CopyTo(memoryStream);
var bytesdecrypt = memoryStream.ToArray();
string result = Encoding.UTF8.GetString(bytesdecrypt);
Console.WriteLine(result);
Console.ReadLine();
}
}
}
}
The problem is probably when I am reading and writing from and to the streams.
There are many problems with this code.
The reason why nothing is decrypted is because you forgot to reset the message stream before doing message.CopyTo(memoryStream), because CopyTo works from the current position and you haven't changed the position after decryption.
You could reset it with
message.Position = 0;
If arbitrary data is encrypted, AES with some mode of operation like CBC is not enough. We generally need some kind of padding scheme. In C# the default scheme is PKCS#7 padding. Unambiguous padding is always added even when the plaintext is already a multiple of the block size. In those cases a full padding block is added.
Now the problem is that you're reading 2048 byte chunks during encryption and decryption, but the encryption produces 2064 byte ciphertext chunks which must be read as such during decryption. This is a simple fix, but it would be better to use streams all the way instead of encrypting such separate chunks.
You're invoking Rfc2898DeriveBytes for every 2048 byte chunk, but it never changes. Either introduce randomness, but actually using a random salt and random IV, or cache the key. (random salt and random IV are still necessary to reach semantic security)
I have error from CryptoStream:
Padding is invalid and cannot be removed.
Code
public MemoryStream EncrypteBytes(Stream inputStream, string passPhrase, string saltValue)
{
RijndaelManaged RijndaelCipher = new RijndaelManaged();
RijndaelCipher.Padding = PaddingMode.PKCS7;
RijndaelCipher.Mode = CipherMode.CBC;
byte[] salt = Encoding.ASCII.GetBytes(saltValue);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, salt, "SHA1", 2);
ICryptoTransform Encryptor = RijndaelCipher.CreateEncryptor(password.GetBytes(32), password.GetBytes(16));
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, Encryptor, CryptoStreamMode.Write);
var buffer = new byte[1024];
var read = inputStream.Read(buffer, 0, buffer.Length);
while (read > 0)
{
cryptoStream.Write(buffer, 0, read);
read = inputStream.Read(buffer, 0, buffer.Length);
}
cryptoStream.FlushFinalBlock();
memoryStream.Position = 0;
return memoryStream;
}
// Example usage: DecryptBytes(encryptedBytes, "SensitivePhrase", "SodiumChloride");
public byte[] DecrypteBytes(MemoryStream memoryStream, string passPhrase, string saltValue)
{
RijndaelManaged RijndaelCipher = new RijndaelManaged();
RijndaelCipher.Padding = PaddingMode.PKCS7;
RijndaelCipher.Mode = CipherMode.CBC;
byte[] salt = Encoding.ASCII.GetBytes(saltValue);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, salt, "SHA1", 2);
ICryptoTransform Decryptor = RijndaelCipher.CreateDecryptor(password.GetBytes(32), password.GetBytes(16));
CryptoStream cryptoStream = new CryptoStream(memoryStream, Decryptor, CryptoStreamMode.Read);
byte[] plainBytes = new byte[memoryStream.Length];
int DecryptedCount = cryptoStream.Read(plainBytes, 0, plainBytes.Length);
return plainBytes;
}
Use PaddingMode.Zeros to fix problem , Following code:
public byte[] EncryptFile(Stream input, string password, string salt)
{
// Essentially, if you want to use RijndaelManaged as AES you need to make sure that:
// 1.The block size is set to 128 bits
// 2.You are not using CFB mode, or if you are the feedback size is also 128 bits
var algorithm = new RijndaelManaged { KeySize = 256, BlockSize = 128 };
var key = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(salt));
algorithm.Key = key.GetBytes(algorithm.KeySize / 8);
algorithm.IV = key.GetBytes(algorithm.BlockSize / 8);
algorithm.Padding = PaddingMode.Zeros;
using (Stream cryptoStream = new MemoryStream())
using (var encryptedStream = new CryptoStream(cryptoStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write))
{
CopyStream(input, encryptedStream);
return ReadToEnd(cryptoStream);
}
}
public byte[] DecryptFile(Stream input, Stream output, string password, string salt)
{
// Essentially, if you want to use RijndaelManaged as AES you need to make sure that:
// 1.The block size is set to 128 bits
// 2.You are not using CFB mode, or if you are the feedback size is also 128 bits
var algorithm = new RijndaelManaged { KeySize = 256, BlockSize = 128 };
var key = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(salt));
algorithm.Key = key.GetBytes(algorithm.KeySize / 8);
algorithm.IV = key.GetBytes(algorithm.BlockSize / 8);
algorithm.Padding = PaddingMode.Zeros;
try
{
using (var decryptedStream = new CryptoStream(output, algorithm.CreateDecryptor(), CryptoStreamMode.Write))
{
CopyStream(input, decryptedStream);
return ReadToEnd(output);
}
}
catch (CryptographicException ex)
{
throw new InvalidDataException("Please supply a correct password");
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
I hop help you.
Please check your pass phrase - it should be same in both methods EncrypteBytes and DecrypteBytes. If both are not same, then it will generate the error.
My problem was I was taking the encryped output in bytes and converting it to a string. The string back to byte array (for decrypting) was not the same. I was using UTF8Encoding, then I tried ASCIIEnconding. Neither worked.
Convert.FromBase64String/ToBase64String worked fine, got rid of the padding issues and actually decrypted the data.
It's work
using (FileStream fs = new FileStream( absolute, FileMode.Open )) {
// create a CryptoStream in read mode
using (CryptoStream cryptoStream = new CryptoStream( fs, decryptor, CryptoStreamMode.Read )) {
int readLength = ( int )fs.Length;
byte[] buffer = new byte[readLength];
cryptoStream.Read( buffer, 0, readLength );
using (MemoryStream ms = new MemoryStream( buffer )) {
BinaryFormatter bf = new BinaryFormatter( );
settings = ( SettingsJson )bf.Deserialize( ms );// Deserialize SettingsJson array
}
}
fs.Close( );
}
I've code to decrypt file in DES Encryption, I try I keep getting the file even though I enter the key at the time when the key decrypt the encrypted differently. But i've get the error.
while ((data = cryptostreamDecr.ReadByte()) != -1) // Message Error : Bad Data.
Which code should I add or change in order to keep the process running decrypt?
private static void DecryptFile(string sInputFilename, string sKey)
{
var DES = new DESCryptoServiceProvider();
DES.Key = Encoding.ASCII.GetBytes(sKey);
DES.IV = Encoding.ASCII.GetBytes(sKey);
ICryptoTransform desdecrypt = DES.CreateDecryptor();
using (var fsread = new FileStream(sInputFilename, FileMode.Open,
FileAccess.ReadWrite))
{
using (var cryptostreamDecr = new CryptoStream(fsread,
desdecrypt,
CryptoStreamMode.Read))
{
int data;
fsread.Flush();
using (var ms = new MemoryStream())
{
while ((data = cryptostreamDecr.ReadByte()) != -1)
{
ms.WriteByte((byte)data);
}
cryptostreamDecr.Close();
using (var fsWrite = new FileStream(sInputFilename, FileMode.Truncate))
{
ms.WriteTo(fsWrite);
ms.Flush();
}
}
}
}
}
Encrypt Code :
public static void EncryptFile(string sInputFilename, string sKey)
{
FileStream fsInput = new FileStream(sInputFilename,
FileMode.Open,
FileAccess.ReadWrite);
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
ICryptoTransform desencrypt = DES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(fsInput,
desencrypt,
CryptoStreamMode.Write);
byte[] bytearrayinput = new byte[fsInput.Length];
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
fsInput.SetLength(0);
cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.FlushFinalBlock();
cryptostream.Close();
fsInput.Close();
}
[EDIT] : remove in Encypt :
cryptostream.FlushFinalBlock();
And add in decrypt
DES.Padding = PaddingMode.None;
64 bits is the only valid key size for the DES encryption algorithm.
8 bits is equal to one ASCII character means 64 bits is equal to 8 Characters.
If you send 8 characters only then check this (C# "Bad Data" exception when decrypting encrypted file). It may solve your problem.
[Edit]
Add DES.Padding = PaddingMode.None; in DecryptFile.