There is a method (Version1) that encodes an input stream and there is a function Decrypt() that successfully decodes encoded data. But when the input data is large there could be an error OutOfMemory (on the line "string textEncrypted = Convert.ToBase64String (ms.ToArray())").
Version1
private static Stream EncryptRijndael1(byte[] key, byte[] iv, Stream plainText)
{
if (plainText == null)
return null;
byte[] bytesEncrypted;
RijndaelManaged rjndl = RijndaelManagedWithConfig(key, iv);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, rjndl.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
byte[] buffer = new byte[16 * 1024];
int readed;
while ((readed = (plainText.Read(buffer, 0, buffer.Length))) > 0)
{
cs.Write(buffer, 0, readed);
}
}
string textEncrypted = Convert.ToBase64String(ms.ToArray());
bytesEncrypted = Encoding.ASCII.GetBytes(textEncrypted);
}
return new MemoryStream(bytesEncrypted);
}
So I modified the method to process an array part by part (chunks).
Here is Version2. It causes an error "offset and length must refer to a position in the string" in the line Convert.ToBase64String (ms.ToArray(), offset, read).
Version2
private static Stream EncryptRijndael2(byte[] key, byte[] iv, Stream plainText)
{
if (plainText == null)
return null;
byte[] bytesEncrypted;
RijndaelManaged rjndl = RijndaelManagedWithConfig(key, iv);
string textEncrypted = String.Empty;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, rjndl.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
byte[] buffer = new byte[16 * 1024];
int readed;
int offset = 0;
while ((readed = (plainText.Read(buffer, 0, buffer.Length))) > 0)
{
cs.Write(buffer, 0, readed);
textEncrypted += Convert.ToBase64String(ms.ToArray(), offset, readed);
offset += readed;
}
}
bytesEncrypted = Encoding.ASCII.GetBytes(textEncrypted);
}
return new MemoryStream(bytesEncrypted);
}
Then I made Version3. It works without errors but the output data length now is bigger than in Version1 having the same input data.
Decryt() function throws an error "The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters."
Version3
private static Stream EncryptRijndael3(byte[] key, byte[] iv, Stream plainText)
{
if (plainText == null)
return null;
byte[] bytesEncrypted;
RijndaelManaged rjndl = RijndaelManagedWithConfig(key, iv);
using (MemoryStream ms = new MemoryStream())
{
string textEncrypted = String.Empty;
using (CryptoStream cs = new CryptoStream(ms, rjndl.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
byte[] buffer = new byte[16*1024];
int readed;
while ((readed = (plainText.Read(buffer, 0, buffer.Length))) > 0)
{
cs.Write(buffer, 0, readed);
}
}
byte[] buffer1 = new byte[16*1024];
int readed1;
using (MemoryStream ms1 = new MemoryStream(ms.ToArray()))
{
while ((readed1 = (ms1.Read(buffer1, 0, buffer1.Length))) > 0)
{
if (readed1 < buffer1.Length)
{
var lastBuf = new List<Byte>();
for (int i = 0; i < readed1; i++)
{
lastBuf.Add(buffer1[i]);
}
textEncrypted += Convert.ToBase64String(lastBuf.ToArray());
continue;
}
textEncrypted += Convert.ToBase64String(buffer1);
}
}
bytesEncrypted = Encoding.ASCII.GetBytes(textEncrypted);
}
return new MemoryStream(bytesEncrypted);
}
My RijndaelManaged
private static RijndaelManaged RijndaelManagedWithConfig(byte[] key, byte[] iv)
{
RijndaelManaged rjndl = new RijndaelManaged();
rjndl.KeySize = 256;
rjndl.BlockSize = 128;
rjndl.Key = key;
rjndl.IV = iv;
rjndl.Mode = CipherMode.CBC;
rjndl.Padding = PaddingMode.PKCS7;
return rjndl;
}
Please help me to get rid of the errors or tell me how to make the Version1 process Convert.ToBase64String data partially.
I have achieved the decision
private static Stream EncryptRijndael(byte[] key, byte[] iv, Stream plainText)
{
if (plainText == null)
return null;
byte[] buffer = new byte[5120 * 1024];
RijndaelManaged rjndl = RijndaelManagedWithConfig(key, iv);
using (var memoryStream = new MemoryStream())
{
int readedBytes;
using (var cs = new CryptoStream(memoryStream, rjndl.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
while ((readedBytes = (plainText.Read(buffer, 0, buffer.Length))) > 0)
{
cs.Write(buffer, 0, readedBytes);
}
}
using (var cryptoMemoryStream = new MemoryStream(memoryStream.ToArray()))
{
using (var base64MemoryStream = new MemoryStream())
{
using (ICryptoTransform transform = new ToBase64Transform())
{
using (var cryptStream = new CryptoStream(base64MemoryStream, transform, CryptoStreamMode.Write))
{
while ((readedBytes = cryptoMemoryStream.Read(buffer, 0, buffer.Length)) > 0)
{
cryptStream.Write(buffer, 0, readedBytes);
}
cryptStream.FlushFinalBlock();
}
return new MemoryStream(base64MemoryStream.ToArray());
}
}
}
}
}
Related
I'm trying to write an encryption/decryption method for practice and after getting an initial run working, I decided to step it up and make it less vulnerable by encrypting the IV into the data. I got that working, and decided to step it up again by introducing an offset for the IV's location in the data, by adding some random data to the left hand side of the IV. Up until this point, everything was working fine, but now I'm receiving an error on decryption that states:
The input data is not a complete block.
Which with my limited knowledge of encryption and decryption is quite useless to me in debugging the issue. I've searched high and low, and none of the answers to this problem, that I've found seem to fix my issue. The answers are typically along the lines of the developer isn't decrypting a byte[] but instead something like a base 64 string.
private static Guid TestGuid = Guid.NewGuid();
private static DateTime Timestamp = DateTime.Now;
private static string key = "PPPQmyuzqKtjzYlWM3mP0aDxaxCzlsACajIkTVN4IjI=";
public static void Main()
{
string data = TestGuid + "|" + Timestamp;
Console.WriteLine("Request Parameter: " + data);
string encryptedData = AESEncrypt(key, data, 1);
Console.WriteLine("Encrypted: " + encryptedData);
string decryptedData = AESDecrypt(key, encryptedData, 1);
Console.WriteLine("Decrypted: " + decryptedData);
}
public static string AESEncrypt(string key, string data, int offset)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentException("Data");
byte[] encryptedData;
byte[] keyData = Convert.FromBase64String(key);
using (Aes algo = Aes.Create())
{
algo.Key = keyData;
algo.GenerateIV();
algo.Padding = PaddingMode.PKCS7;
byte[] iv = new byte[offset + 16];
Random r = new Random();
using (MemoryStream ms = new MemoryStream())
{
for (int i = 0; i < offset; i++)
iv[i] = (byte)r.Next(1, 200);
for (int i = 0; i < algo.IV.Length; i++)
iv[offset + i - 1] = algo.IV[i];
ICryptoTransform encryptor = algo.CreateEncryptor(algo.Key, algo.IV);
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (BinaryWriter bw = new BinaryWriter(cs))
{
bw.Write(iv, 0, offset);
ms.Write(iv, offset, algo.IV.Length);
bw.Write(data);
cs.FlushFinalBlock();
}
encryptedData = ms.ToArray();
}
}
}
if (encryptedData != null)
return Convert.ToBase64String(encryptedData);
throw new Exception("An unxpected error occurred and the provided data was not encrypted.");
}
public static string AESDecrypt(string key, string data, int offset)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentException("Data");
string decryptedData;
byte[] keyData = Convert.FromBase64String(key);
using (Aes algo = Aes.Create())
{
algo.Key = keyData;
algo.Padding = PaddingMode.PKCS7;
byte[] decodedData = Convert.FromBase64String(data);
using (MemoryStream ms = new MemoryStream(decodedData))
{
byte[] ivData = new byte[offset + 16];
ms.Read(ivData, 0, offset + 16);
List<byte> iv = new List<byte>();
for (int i = offset - 1; i < ivData.Length - 1; i++)
iv.Add(ivData[i]);
algo.IV = iv.ToArray();
ICryptoTransform decryptor = algo.CreateDecryptor(algo.Key, algo.IV);
List<byte> dataToDecrypt = new List<byte>();
for (int i = 0; i + offset < decodedData.Length; i++)
dataToDecrypt.Add(decodedData[i + offset]);
using (MemoryStream ds = new MemoryStream(dataToDecrypt.ToArray()))
{
using (CryptoStream cs = new CryptoStream(ds, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decryptedData = sr.ReadToEnd();
}
}
}
}
}
if (!string.IsNullOrWhiteSpace(decryptedData))
return decryptedData;
throw new Exception("An unxpected error occurred and the provided data was not decrypted.");
}
What is causing this error, why is it causing the error, how do I resolve the error, and why does that resolution work?
Messing with the final encryption stream during the encryption process (feeding into it raw bytes) is a fatal mistake and should be avoided
The problem in question occurs because
bw.Write(iv, 0, offset);
ms.Write(iv, offset, algo.IV.Length);
Feeding the first random bytes of the modified IV to the encryption stream and the rest of it to the raw output stream, the first bytes of the modified iv will not be written to the memory stream immediately and instead will be part of the first encryption block, thus, the size of the cipher during decryption will lack some bytes, for example it lacks 1 byte where offset = 1
But you expect them to be written immediately as independent bytes, because in the decryption part you read offset + 16 bytes from the stream and thus you read into the encrypted block and cause it to be less than the block size for AES. You can see this if you debug the code. The final size of the encrypted bytes is 0x50 while the size of bytes for decryption is 0x50 - offset = 0x4f (offset = 1)
For solution,
You can derive the IV from the Key (which is not secure if you are reusing the key) and will not have to include it in your cipher.
You can prepend the IV (and your random bytes) to your encrypted buffer and read it first then use it for decryption,
like:
public static string AESEncrypt(string key, string data, int offset)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentException("Data");
byte[] encryptedData;
byte[] keyData = Convert.FromBase64String(key);
using (Aes algo = Aes.Create())
{
algo.Key = keyData;
algo.GenerateIV();
algo.Padding = PaddingMode.PKCS7;
Random r = new Random();
using (MemoryStream ms = new MemoryStream())
{
for (int i = 0; i < offset; i++)
{
ms.WriteByte((byte)r.Next(0, 200));
}
ms.Write(algo.IV, 0, 16);
ICryptoTransform encryptor = algo.CreateEncryptor(algo.Key, algo.IV);
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (BinaryWriter bw = new BinaryWriter(cs))
{
bw.Write(data);
cs.FlushFinalBlock();
}
encryptedData = ms.ToArray();
}
}
}
if (encryptedData != null)
return Convert.ToBase64String(encryptedData);
throw new Exception("An unxpected error occurred and the provided data was not encrypted.");
}
public static string AESDecrypt(string key, string data, int offset)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentException("Data");
string decryptedData;
byte[] keyData = Convert.FromBase64String(key);
using (Aes algo = Aes.Create())
{
algo.Key = keyData;
algo.Padding = PaddingMode.PKCS7;
byte[] decodedData = Convert.FromBase64String(data);
using (MemoryStream ms = new MemoryStream(decodedData))
{
for (int i = 0; i < offset; i++) ms.ReadByte();
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
algo.IV = iv.ToArray();
ICryptoTransform decryptor = algo.CreateDecryptor(algo.Key, algo.IV);
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decryptedData = sr.ReadToEnd();
}
}
}
}
if (!string.IsNullOrWhiteSpace(decryptedData))
return decryptedData;
throw new Exception("An unxpected error occurred and the provided data was not decrypted.");
}
static SymmetricAlgorithm encryption;
static string password = "SBC";
static string salt = "ash";
public Decryption()
{
encryption = new RijndaelManaged();
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(salt));
encryption.Key = key.GetBytes(encryption.KeySize / 8);
encryption.IV = key.GetBytes(encryption.BlockSize / 8);
encryption.Padding = PaddingMode.PKCS7;
}
public void Decrypt(Stream inStream, Stream OutStream)
{
ICryptoTransform encryptor = encryption.CreateDecryptor();
inStream.Position = 0;
CryptoStream encryptStream1 = new CryptoStream(OutStream, encryptor, CryptoStreamMode.Write);
CopyTo(inStream, encryptStream1);
encryptStream1.FlushFinalBlock();
encryptStream1.Close();
inStream.Close();
OutStream.Close();
}
public void CopyTo(Stream input, Stream output)
{
// This method exists only in .NET 4 and higher
byte[] buffer = new byte[4 * 1024];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
{
output.Write(buffer, 0, bytesRead);
}
}
In my windows form load i just create a thread and call the function to decrypt file, this is thread function
Thread objthreadhtml = new Thread(new ThreadStart(JsHtmlDecrypt));
objthreadhtml.IsBackground = true;
objthreadhtml.Name = "HtmlJsDecrypt";
objthreadhtml.Priority = ThreadPriority.Highest;
objthreadhtml.Start();
below function is decrypt function
public static void JsHtmlDecrypt()
{
string startPathForHtml = Application.LocalUserAppDataPath.Replace("\\OfflineApplication\\OfflineApplication\\1.0.0.0", "").ToString() + "\\Apps\\Html\\";
var directoryPathForHtml = new DirectoryInfo(startPathForHtml);
foreach (FileInfo fileForHtml in directoryPathForHtml.GetFiles())
{
FileStream inFsForHtml = fileForHtml.OpenRead();
FileInfo inforFHtml = new FileInfo(fileForHtml.FullName.Replace(fileForHtml.Extension, ".html"));
FileStream outFsForHtml = inforFHtml.Create();
UnZipDecryptionEncryption.Decryption m_decryption1 = new Decryption();
m_decryption1.Decrypt(inFsForHtml, outFsForHtml);
inFsForHtml.Close();
outFsForHtml.Close();
UnZipDecryptionEncryption.DeleteZipandFiles m_delete1 = new DeleteZipandFiles();
m_delete1.DeleteFiles(fileForHtml.FullName);
}
}
Here i get the error padding is invalid in the line
encryptStream1.FlushFinalBlock();
Please help me someone how to solve this i am stuck in it.
Your "decrypt" function is trying to do the opposite of what you want: encrypt the data:
CryptoStream encryptStream1 = new CryptoStream(OutStream, encryptor, CryptoStreamMode.Write);
What you want, I assume, is to decrypt it (my code uses byte arrays as input/output, so you may want to modify that):
ICryptoTransform decryptor = encryption.CreateDecryptor();
// byte[] (cipherText) <-- encryted text
MemoryStream memoryStream = new MemoryStream(cipherText);
// here is the most important part: CryptoStreamMode.Read
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherText.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
// my text uses UTF8 encoding, so to get the plain text as string:
string result = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
I have used Rijndael Algorithm for Encryption and decryption.
The Decryption works fine when i do it with Encryption.
But when i try to do Decryption alone it returns something like this
J˿m"�e��c4�ħ�dB̵��Dq#W�.
Also i have used two buttons one is for encryption and another one is for decryption and called the methods when button clicks.
I cannot able to get any idea regarding why the output is returning like this. Even i used same convertion(UTF8 Encoding) for both methods.
Please help me to solve this problem.
Below is my code:
public partial class Form1 : Form
{
private RijndaelManaged myRijndael = new RijndaelManaged();
private int iterations;
private byte [] salt;
public Form1(string strPassword)
{
myRijndael.BlockSize = 128;
myRijndael.KeySize = 128;
myRijndael.IV = HexStringToByteArray("e84ad660c4721ae0e84ad660c4721ae0");
myRijndael.Padding = PaddingMode.PKCS7;
myRijndael.Mode = CipherMode.CBC;
iterations = 1000;
salt = System.Text.Encoding.UTF8.GetBytes("cryptography123example");
myRijndael.Key = GenerateKey(strPassword);
}
public string Encrypt(string strPlainText)
{
byte[] strText = new System.Text.UTF8Encoding().GetBytes(strPlainText);
MemoryStream ms = new MemoryStream();
ICryptoTransform transform = myRijndael.CreateEncryptor();
CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Write);
cs.Write(strText, 0, strText.Length);
cs.FlushFinalBlock();
return Convert.ToBase64String(ms.ToArray());
}
public string Decrypt(string encryptedText)
{
var encryptedBytes = Convert.FromBase64String(encryptedText);
MemoryStream ms = new MemoryStream();
ICryptoTransform transform = myRijndael.CreateDecryptor();
CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Write);
cs.Write(encryptedBytes, 0, encryptedBytes.Length);
return System.Text.Encoding.UTF8.GetString(ms.ToArray());
}
public static byte[] HexStringToByteArray(string strHex)
{
dynamic r = new byte[strHex.Length / 2];
for (int i = 0; i <= strHex.Length - 1; i += 2)
{
r[i / 2] = Convert.ToByte(Convert.ToInt32(strHex.Substring(i, 2), 16));
}
return r;
}
private byte[] GenerateKey(string strPassword)
{
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(System.Text.Encoding.UTF8.GetBytes(strPassword), salt, iterations);
return rfc2898.GetBytes(128 / 8);
}
private void button1_Click(object sender, EventArgs e)
{
EncryptOutput.Text = Encrypt(EncryptInput.Text);
}
private void button2_Click(object sender, EventArgs e)
{
DecryptOutput.Text = Decrypt(DecryptInput.Text);
}
}
Try this code:
public string Encrypt(string strPlainText) {
byte[] strText = System.Text.Encoding.UTF8.GetBytes(strPlainText);
using (ICryptoTransform encryptor = myRijndael.CreateEncryptor())
using (MemoryStream input = new MemoryStream(strText))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(output, encryptor, CryptoStreamMode.Write)) {
input.CopyTo(cs);
cs.FlushFinalBlock();
return Convert.ToBase64String(output.GetBuffer(), 0, (int)output.Length);
}
}
public string Decrypt(string encryptedText) {
byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
using (ICryptoTransform decryptor = myRijndael.CreateDecryptor())
using (MemoryStream input = new MemoryStream(encryptedBytes))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(input, decryptor, CryptoStreamMode.Read)) {
cs.CopyTo(output);
return System.Text.Encoding.UTF8.GetString(output.GetBuffer(), 0, (int)output.Length);
}
}
public static byte[] HexStringToByteArray(string strHex) {
var r = new byte[strHex.Length / 2];
for (int i = 0; i < strHex.Length; i += 2) {
r[i / 2] = byte.Parse(strHex.Substring(i, 2), NumberStyles.HexNumber);
}
return r;
}
Please, remember using the using pattern... And dynamic should be used only in very special cases.
Note that the Encrypt is doable with one less Stream, in a very similar way to the one you wrote it:
public string Encrypt(string strPlainText) {
byte[] strText = System.Text.Encoding.UTF8.GetBytes(strPlainText);
using (ICryptoTransform encryptor = myRijndael.CreateEncryptor())
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(output, encryptor, CryptoStreamMode.Write)) {
cs.Write(strText, 0, strText.Length);
cs.FlushFinalBlock();
return Convert.ToBase64String(output.GetBuffer(), 0, (int)output.Length);
}
}
but the Decrypt needs two Stream, because the CryptoStream needs a Stream as a parameter, containing the encrypted data, and it is easier to write its output (of which you don't know the exact lenth, thanks to padding) to another stream.
cs.FlushFinalBlock(); forgotten in Decrypt()? I just round-tripped a test string with your code once I fixed that
I am trying to manually decrypt data which was encrypted with MachineKey.Protect(). I am using AES and SHA1 algoritms.
// unencrypted input in HEX: 010203
// AES key in HEX: CCA0DC9874B3F9E679E0A576F77EDF9B121CAB2F9A363A4EAF99976F7B51FA89
// want to decrypt this: A738E5F98920E37AB14C5F4332D4C7F0EC683680AAA0D34B806E75DECF04B7A3DB651E688B563F77BA107FB15990C88FB8023386
Here is an example.
// Some input data
var input = new byte[] { 1, 2, 3 };
// this just works fine
var protectedData = System.Web.Security.MachineKey.Protect(input, "ApplicationCookie", "v1");
// protectedData in hex: A738E5F98920E37AB14C5F4332D4C7F0EC683680AAA0D34B806E75DECF04B7A3DB651E688B563F77BA107FB15990C88FB8023386
// works
var unprotectedInput = System.Web.Security.MachineKey.Unprotect(protectedData, "ApplicationCookie", "v1");
// now lets do it manually
// in web.config machineKey is configured: AES and SHA1
var algorithm = new AesManaged();
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
var validationAlgorithm = new HMACSHA1();
// this is the key from web.config
var key = HexToBinary("CCA0DC9874B3F9E679E0A576F77EDF9B121CAB2F9A363A4EAF99976F7B51FA89");
using (SymmetricAlgorithm encryptionAlgorithm = algorithm)
{
encryptionAlgorithm.Key = key;
int offset = encryptionAlgorithm.BlockSize / 8; //16
int buffer1Count = validationAlgorithm.HashSize / 8; // 20
int count = checked(protectedData.Length - offset - buffer1Count); // 16
byte[] numArray = new byte[offset];
Buffer.BlockCopy((Array)protectedData, 0, (Array)numArray, 0, numArray.Length);
encryptionAlgorithm.IV = numArray; // in HEX: A738E5F98920E37AB14C5F4332D4C7F0
using (MemoryStream memoryStream = new MemoryStream())
{
using (ICryptoTransform decryptor = encryptionAlgorithm.CreateDecryptor())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(protectedData, offset, count);
// Padding is invalid and cannot be removed.
cryptoStream.FlushFinalBlock();
var result = memoryStream.ToArray();
}
}
}
}
I am getting an exception (padding is not right).
I do not know what else to try...
This is the code for MachineKey.Protect, https://msdn.microsoft.com/cs-cz/library/system.web.security.machinekey.protect(v=vs.110).aspx
public byte[] Protect(byte[] clearData)
{
// this is AESManaged
using (SymmetricAlgorithm encryptionAlgorithm = this._cryptoAlgorithmFactory.GetEncryptionAlgorithm())
{
// this is our key
encryptionAlgorithm.Key = this._encryptionKey.GetKeyMaterial();
if (this._predictableIV)
encryptionAlgorithm.IV = CryptoUtil.CreatePredictableIV(clearData, encryptionAlgorithm.BlockSize);
else
encryptionAlgorithm.GenerateIV();
byte[] iv = encryptionAlgorithm.IV;
using (MemoryStream memoryStream = new MemoryStream())
{
memoryStream.Write(iv, 0, iv.Length);
using (ICryptoTransform encryptor = encryptionAlgorithm.CreateEncryptor())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(clearData, 0, clearData.Length);
cryptoStream.FlushFinalBlock();
using (KeyedHashAlgorithm validationAlgorithm = this._cryptoAlgorithmFactory.GetValidationAlgorithm())
{
validationAlgorithm.Key = this._validationKey.GetKeyMaterial();
byte[] hash = validationAlgorithm.ComputeHash(memoryStream.GetBuffer(), 0, checked ((int) memoryStream.Length));
memoryStream.Write(hash, 0, hash.Length);
return memoryStream.ToArray();
}
}
}
}
}
}
The decryption key is wrong.
MachineKey.Protect/UnProtect() modifies the key before using it.
It is doing something like this:
public static byte[] DeriveKey(byte[] key, int keySize, string primaryPurpose, params string[] specificPurposes)
{
var secureUtf8Encoding = new UTF8Encoding(false, true);
var hash = new HMACSHA512(key);
using (hash)
{
var label = secureUtf8Encoding.GetBytes(primaryPurpose);
byte[] context;
using (var memoryStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(memoryStream, secureUtf8Encoding))
{
foreach (var str in specificPurposes)
binaryWriter.Write(str);
context = memoryStream.ToArray();
}
}
return DeriveKeyImpl(hash, label, context, keySize);
}
}
public static byte[] DeriveKeyImpl(HMAC hmac, byte[] label, byte[] context, int keyLengthInBits)
{
int count1 = label != null ? label.Length : 0;
int count2 = context != null ? context.Length : 0;
byte[] buffer = new byte[checked(4 + count1 + 1 + count2 + 4)];
if (count1 != 0)
Buffer.BlockCopy((Array)label, 0, (Array)buffer, 4, count1);
if (count2 != 0)
Buffer.BlockCopy((Array)context, 0, (Array)buffer, checked(5 + count1), count2);
WriteUInt32ToByteArrayBigEndian(checked((uint)keyLengthInBits), buffer, checked(5 + count1 + count2));
int dstOffset = 0;
int val1 = keyLengthInBits / 8;
byte[] numArray = new byte[val1];
uint num = 1;
while (val1 > 0)
{
WriteUInt32ToByteArrayBigEndian(num, buffer, 0);
byte[] hash = hmac.ComputeHash(buffer);
int count3 = Math.Min(val1, hash.Length);
Buffer.BlockCopy((Array)hash, 0, (Array)numArray, dstOffset, count3);
checked { dstOffset += count3; }
checked { val1 -= count3; }
checked { ++num; }
}
return numArray;
}
It is very important to specify the right purpose. For standard MachineKey.Protect primary reason is "User.MachineKey.Protect". The key size for this example is 256 (in bits).
I got a problem with decrypting using CryptoStream to MemoryStream. When I decrypt a data that converted from a string to byte[] it decrypts normally, but when I pass an image data to the decryptor it returns x00 filled stream with the same length as the image data. Btw, no exception thrown.
private SymmetricAlgorithm crypto;
...
public byte[] Encrypt(byte[] Stream)
{
ICryptoTransform encryptor = crypto.CreateEncryptor();
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
int written = 0;
int length = Stream.Length;
int blockSize = crypto.BlockSize / 8;
while (written < length)
{
int toWrite = Math.Min(blockSize, length - written);
if ((length - written) > blockSize)
{
csEncrypt.Write(Stream, written, blockSize);
written += blockSize;
csEncrypt.Flush();
}
else
{
csEncrypt.Write(Stream, written, toWrite);
written += toWrite;
csEncrypt.FlushFinalBlock();
}
}
resBytes = msEncrypt.ToArray();
}
}
return resBytes;
}
...
public byte[] Decrypt(byte[] Stream)
{
ICryptoTransform decryptor = crypto.CreateDecryptor();
MemoryStream decrypted = new MemoryStream();
using (MemoryStream msDecrypt = new MemoryStream(Stream))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
csDecrypt.CopyTo(decrypted, crypto.BlockSize);
resBytes = decrypted.ToArray();
decrypted.Dispose();
resBytes = decrypted.ToArray();
return resBytes;
}
}
}