Related
I just started with Cryptography in C# an tried to first encrypt and then decrypt a file. But during the decryption, in the function
private static byte[] Decrypt(byte[] inputBuffer)
, at
byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
I am getting the following Exception:
System.Security.Cryptography.CryptographicException: "Invalid data".
But why is that?
This is the function to read the file:
private static void DecryptFile()
{
byte[] buffer = new byte[4096];
byte[] decryptionBuffer = new byte[4096];
int bytesRead = 0;
using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Encrypted.txt"))
{
using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\\Decrypt.mp3"))
{
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
decryptionBuffer = Decrypt(buffer);
outputStream .Write(decryptionBuffer, 0, decryptionBuffer.Length);
}
}
Console.WriteLine("Done.");
}
}
This is the function to decrypt the file, the key and the initialization vector:
private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] Decrypt(byte[] inputBuffer)
{
SymmetricAlgorithm algorithm = DES.Create();
ICryptoTransform transform = algorithm.CreateDecryptor(key, iv);
//here the exception is triggered
byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
return outputBuffer;
}
this is how the file was encrypted:
private static void EncryptFile()
{
byte[] buffer = new byte[4096];
byte[] enryptionBuffer = new byte[4096];
int bytesRead = 0;
using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Test.txt"))
{
using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\\Encrypted.txt"))
{
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
encryptionBuffer = Encrypt(buffer);
outputStream .Write(encryptionBuffer, 0, encryptionBuffer.Length);
}
}
Console.WriteLine("Done.");
}
}
//Key and initialization vector are the same
private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] Encrypt(byte[] inputBuffer)
{
SymmetricAlgorithm algorithm = DES.Create();
ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
return outputBuffer;
}
Your encryption code produces larger output buffers than your input buffers. If you independently encrypt 4096 bytes at a time, it produces 4104 bytes of output1. If you wish to continue to independently encrypt each block of your file, you need to change your decryption code to use 4104 byte buffers when it's working in chunks.
Ideally though, this "encrypt each block separately" isn't a requirement. If that's the case, create your transform object once, not once each time through your loop. And use Transform rather than TransformFinalBlock up until you know you've reached the end of the file (pay attention that they return very different things however).
You're also ignoring bytesRead which tells you how much of your buffer is filled with useful data. You need to use that as well and don't make your final encryption round be x many bytes which are the last bytes of the file and bufferSize - x bytes of the previous block of data from the file.
I would probably look to instead create a CryptoStream that wraps one of your FileStream objects and then use Stream.CopyTo or moral equivalents to do this work. Let libraries worry about managing buffers, loops, etc.
And finally, ideally, you recognize that it's 2019 and so very far from appropriate to be writing new code that uses DES for encryption2
1This program, if you set a breakpoint on the Console.ReadLine line, has c containing 4104 bytes:
using System;
using System.Security.Cryptography;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var b = new byte[4096];
var c = Encrypt(b);
Console.ReadLine();
}
private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] Encrypt(byte[] inputBuffer)
{
SymmetricAlgorithm algorithm = DES.Create();
ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
byte[] outputBuffer = transform.TransformFinalBlock(
inputBuffer,
0,
inputBuffer.Length);
return outputBuffer;
}
}
2So my EnryptFile in its entirety would be:
private static void EncryptFile()
{
using (var inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Test.txt"))
using (var outputStream = File.Create(Environment.CurrentDirectory + "\\Encrypted.txt"))
using (var aes = Aes.Create())
using (var cStream = new CryptoStream(
inputStream,
aes.CreateEncryptor(key, iv),
CryptoStreamMode.Read))
{
cStream.CopyTo(outputStream);
}
}
Or an async variant which which uses await cStream.CopyToAsync(outputStream); as it's innermost statement. DecryptFile would be similarly simplified.
I was facing the same problem then I created a customized function for encryption/decryption and using this function also supports large files because we are reading and writing a file chunk by chunk. there is an EncryptMode that is what do you want by this method like if you want to encryption then send _mode as _mode.ENCRYPT and if you want decryption then send _mode as _mode.DECRYPT.
private enum EncryptMode { ENCRYPT, DECRYPT };
public void encryptDecryptChunkByChunk(string _inputPath, string _outputPath, string _encryptionKey, EncryptMode _mode, string _initVector)
{
string _out = "";// output string
//_encryptionKey = MD5Hash (_encryptionKey);
_pwd = Encoding.UTF8.GetBytes(_encryptionKey);
_ivBytes = Encoding.UTF8.GetBytes(_initVector);
int len = _pwd.Length;
if (len > _key.Length)
{
len = _key.Length;
}
int ivLenth = _ivBytes.Length;
if (ivLenth > _iv.Length)
{
ivLenth = _iv.Length;
}
Array.Copy(_pwd, _key, len);
Array.Copy(_ivBytes, _iv, ivLenth);
_rcipher.Key = _key;
_rcipher.IV = _iv;
if (_mode.Equals(EncryptMode.ENCRYPT))
{
//encrypt
using (FileStream fs = new FileStream(_inputPath, FileMode.Open, FileAccess.Read))
{
using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()))
{
System.IO.StreamWriter file = new System.IO.StreamWriter(_outputPath);
try
{
byte[] chunk;
chunk = br.ReadBytes(CHUNK_SIZE);
while (chunk.Length > 0)
{
var base641 = Convert.ToBase64String(chunk);
//DumpBytes(chunk, chunk.Length);
chunk = br.ReadBytes(CHUNK_SIZE);
var base64 = Convert.ToBase64String(chunk);
byte[] plainText = _rcipher.CreateEncryptor().TransformFinalBlock(_enc.GetBytes(base641), 0, base641.Length);
var bas64Encrypted = Convert.ToBase64String(plainText);
//fsCrypt.Write(bas64Encrypted);
file.WriteLine(bas64Encrypted);
}
file.Close();
}
catch (Exception ex)
{
file.Close();
}
}
}
}
if (_mode.Equals(EncryptMode.DECRYPT))
{
FileStream fsOut = new FileStream(_outputPath, FileMode.OpenOrCreate, FileAccess.Write);
try
{
foreach (string line in File.ReadLines(_inputPath))
{
// Process your line here....
var p = line;
var x2 = Convert.FromBase64String(p);
byte[] plainText = _rcipher.CreateDecryptor().TransformFinalBlock(x2, 0, x2.Length);
var y1 = _enc.GetString(plainText);
var y2 = Convert.FromBase64String(y1);
fsOut.Write(y2, 0, y2.Length);
}
fsOut.Close();
}
catch (Exception ex)
{
fsOut.Close();
}
}
_rcipher.Dispose();
}
I am trying to sign a PDF Document using two web services in two servers. But it is showing "Document has been altered or corrupt since it was signed" in Adobe Reader. Can anybody suggest what is wrong in following code.
PROCEDURE
1. Web service (WS) on Server A, Generate hash from PDF and sent to WS on Server B for signing.
2. WS on Server B signs hash.
3. WS on Server A receives signed hash and Embed in PDF document.
CODE
GENERATE HASH
private PDFHashData generateHash(byte[] content, string userName)
{
PdfReader reader = new PdfReader(content);
MemoryStream ms = new MemoryStream();
PdfStamper stamper = PdfStamper.CreateSignature(reader, ms, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.SetVisibleSignature(new Rectangle(500, 150, 400, 200), 1, signatureFieldName);
appearance.SignDate = DateTime.Now;
appearance.Reason = Reason;
appearance.Location = Location;
appearance.Contact = Contact;
StringBuilder buf = new StringBuilder();
buf.Append("Digitally signed by");
buf.Append("\n");
buf.Append(userName);
buf.Append("\n");
buf.Append("Date: " + appearance.SignDate);
appearance.Layer2Text = buf.ToString();
appearance.Acro6Layers = true;
appearance.CertificationLevel = 0;
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
byte[] hash = SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
StringBuilder hex = new StringBuilder(hash.Length * 2);
foreach (byte b in hash)
hex.AppendFormat("{0:x2}", b);
PDFHashData phData= new PDFHashData();
phData.Hash = hex.ToString();
phData.Content = Convert.ToBase64String(ms.ToArray());
return phData;
}
SIGN HASH
byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
private Stream getCertificate()
{
// Base 64 byte - PFX file with private key
return new MemoryStream(Convert.FromBase64String("..................................AgIEAA=="));
}
protected void Page_Load(object sender, EventArgs e)
{
Stream stream = Request.InputStream;
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
byte[] hash = StringToByteArray(Encoding.UTF8.GetString(buffer));
Pkcs12Store store = new Pkcs12Store(getCertificate(), "*******".ToCharArray());
String alias = "";
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
X509CertificateEntry[] chain = store.GetCertificateChain(alias);
List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
foreach (X509CertificateEntry en in chain)
{
c.Add(en.Certificate);
}
PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA1");
String hashAlgorithm = signature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, c, hashAlgorithm, false);
DateTime signingTime = DateTime.Now;
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
Response.Write(Convert.ToBase64String(sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS)));
}
EMBED SIGNATURE TO PDF
private byte[] signPDF(byte[] content, string userName, byte[] pk)
{
PdfReader reader = new PdfReader(content);
MemoryStream os = new MemoryStream();
IExternalSignatureContainer external = new MyExternalSignatureContainer(pk);
MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
return os.ToArray();
}
For those who are interested I am posting the answer.
I ended up using itextsharp 5.5.10. Code is given below,
Initialize PDF object
public PDFSigning(byte[] Content, string UserName)
{
content = Content;
reader = new PdfReader(content);
ms = new MemoryStream();
stamper = PdfStamper.CreateSignature(reader, ms, '\0');
appearance = stamper.SignatureAppearance;
userName = UserName;
}
private Stream getCertificate()
{
return new MemoryStream(Convert.FromBase64String("************************=="));
}
Generate hash
private string generateHash()
{
appearance.SetVisibleSignature(new Rectangle(500, 150, 400, 200), 1, signatureFieldName);
appearance.SignDate = DateTime.Now;
appearance.Reason = Reason;
appearance.Location = Location;
appearance.Contact = Contact;
StringBuilder buf = new StringBuilder();
buf.Append("Digitally signed by");
buf.Append("\n");
buf.Append(userName);
buf.Append("\n");
buf.Append("Date: " + appearance.SignDate);
appearance.Layer2Text = buf.ToString();
appearance.Acro6Layers = true;
appearance.CertificationLevel = 0;
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
{
Date = new PdfDate(appearance.SignDate),
Name = userName
};
dic.Reason = appearance.Reason;
dic.Location = appearance.Location;
dic.Contact = appearance.Contact;
appearance.CryptoDictionary = dic;
Dictionary<PdfName, int> exclusionSizes = new Dictionary<PdfName, int>();
exclusionSizes.Add(PdfName.CONTENTS, (csize * 2) + 2);
appearance.PreClose(exclusionSizes);
HashAlgorithm sha = new SHA256CryptoServiceProvider();
Stream s = appearance.GetRangeStream();
int read = 0;
byte[] buff = new byte[0x2000];
while ((read = s.Read(buff, 0, 0x2000)) > 0)
{
sha.TransformBlock(buff, 0, read, buff, 0);
}
sha.TransformFinalBlock(buff, 0, 0);
StringBuilder hex = new StringBuilder(sha.Hash.Length * 2);
foreach (byte b in sha.Hash)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
Sign hash
public byte[] SignMsg(string hexhash)
{
byte[] hash = hexToByteArray(hexhash);
Pkcs12Store store = new Pkcs12Store(getCertificate(), "*********".ToCharArray());
String alias = "";
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
X509CertificateEntry[] chain = store.GetCertificateChain(alias);
List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
foreach (X509CertificateEntry en in chain)
{
c.Add(en.Certificate);
}
PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA256");
String hashAlgorithm = signature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, c, hashAlgorithm, false);
DateTime signingTime = DateTime.Now;
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
}
Sign PDF
private byte[] signPDF(byte[] pk)
{
byte[] paddedSig = new byte[csize];
System.Array.Copy(pk, 0, paddedSig, 0, pk.Length);
PdfDictionary dic2 = new PdfDictionary();
dic2.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true));
appearance.Close(dic2);
//System.IO.File.WriteAllBytes(System.Web.HttpContext.Current.Server.MapPath("~/temp.pdf"), ms.ToArray());
return ms.ToArray();
}
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 have implemented a class as shown below which is responsible for encrypting/decrypting the text data to a utility class called TextEncryptionData.
It works fine for %99.99 of the time. But sometime (very rare) it fails to decrypt the text and I don't know why it happens. Is there any problem in the way I implemented it?
public class SubscriptionStateEncryptionLogic : IEncryptionLogic
{
private static byte[] KEY = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 71 };
private static byte[] IV = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8};
public EncryptionData Encrypt(EncryptionData source)
{
if (source == null)
{
return null;
}
try
{
TextEncryptionData data = source as TextEncryptionData;
using (TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider())
{
cryptoProvider.KeySize = 128;
cryptoProvider.Key = KEY;
cryptoProvider.IV = IV;
cryptoProvider.Mode = CipherMode.CBC;
cryptoProvider.Padding = PaddingMode.PKCS7;
ICryptoTransform transform = cryptoProvider.CreateEncryptor();
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
using (StreamWriter writer = new StreamWriter(cryptoStream))
{
writer.Write(data.Data);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return new TextEncryptionData() { Data = Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length) };
}
}
}
}
}
catch (Exception ex)
{
throw new SubscriptionStateEncryptionException("Error in encrypting the subscription status.", ex);
}
}
public EncryptionData Decrypt(EncryptionData source)
{
if (source == null)
{
return null;
}
try
{
TextEncryptionData data = source as TextEncryptionData;
using (TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider())
{
cryptoProvider.KeySize = 128;
cryptoProvider.Key = KEY;
cryptoProvider.IV = IV;
cryptoProvider.Mode = CipherMode.CBC;
cryptoProvider.Padding = PaddingMode.PKCS7;
ICryptoTransform transform = cryptoProvider.CreateDecryptor();
using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(data.Data)))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cryptoStream))
{
return new TextEncryptionData() { Data = reader.ReadToEnd() };
}
}
}
}
}
catch (Exception ex)
{
throw new SubscriptionStateEncryptionException("Error in decrypting the subscription status.", ex);
}
}
}
Why can't I combine encryption with file storage, when they work individually?
I'm writing some object properties to a file. First I convert properties to byte array and then I put the whole array (for "one object") together and encrypt it via MemoryStream using Aes. I know about serialization and other possibilities but I really need to do it this way. In some other method I read this file in chunks ("objects"), decrypt it and then reconstruct object properties from byte array. The thing is that only the very first record ("object") gets decrypted and reconstructed normally / correctly. All others get messed up data (int gets value 48464 instead of 2, String showing odd signs, double is -3.16...E-161 instead of 20...).
And I have no idea why. I tried everything I could possibly think of. If I comment out the encryption and decryption everything works, so it is not a problem with writing & reading. If I place the code for decryption and reconstruction of objects just below the code for encryption (so that I decrypt the data chunk which gets written) it decrypts and reconstructs everything properly, thus it shouldn't be a problem with decryption and reconstruction. But when it is all together it just messes up. I'm really lost here.
Please don't focus on the way I manipulate with the data it's really not important right now and I have my reasons why I do it this way.
Here is the whole code for saving to file:
//constant for setting inUse
byte setInUse = 0x80; //1000 0000
//constant for adding spaces to name (string)
byte[] space = Encoding.UTF8.GetBytes(" ");
//result
byte[] data = new byte[32];
//setup encryption (AES)
SymmetricAlgorithm aes = Aes.Create();
byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 };
byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
aes.Padding = PaddingMode.None;
ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);
//setup file stream for saving data
FileStream fStream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 1024, false);
if(writeIndex != 0)
fStream.Position = writeIndex +1;
fStream.Position = 0; //delete me
foreach(Article article in articles)
{
if(article.MyIsNew)
{
article.MyInUseChanged = false;
article.MyPriceChanged = false;
//convert article to byte array
//id
byte[] id = BitConverter.GetBytes(Convert.ToUInt16(article.MyId));
//in use
if (article.MyInUse)
id[0] = (byte)( id[0] | setInUse);
data[0] = id[0];
data[1] = id[1];
//stock
byte[] stock = BitConverter.GetBytes(article.MyStock);
data[2] = stock[0];
data[3] = stock[1];
data[4] = stock[2];
data[5] = stock[3];
data[6] = stock[4];
data[7] = stock[5];
data[8] = stock[6];
data[9] = stock[7];
//name
byte[] name = Encoding.UTF8.GetBytes(article.MyName);
int counter = 10;
for (int i = 0; i < name.Length; i++)
{
data[counter] = name[i];
counter++;
}
//adding spaces
int numToAdd = 22-name.Length;
for (int i = 0; i < numToAdd; i++)
{
data[counter] = space[0];
}
//encrypt
MemoryStream m = new MemoryStream();
using (Stream c = new CryptoStream(m, encryptor, CryptoStreamMode.Write))
c.Write(data, 0, data.Length);
byte[] original = new byte[32];
original = m.ToArray();
fStream.Write(original, 0, original.Length);
}
else if (article.MyInUseChanged)
{
}
if (article.MyPriceChanged)
{
}
}
fStream.Flush();
fStream.Close();
fStream.Dispose();
And here is the whole code for loading:
String fileName = path + "\\articles";
//load data
if (File.Exists(fileName))
{
FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
//setup encryption (AES)
SymmetricAlgorithm aes = Aes.Create();
byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 };
byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
aes.Padding = PaddingMode.None;
ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);
//constant for extracting inUse
byte inUseConst = 0x80;
//constant for extracting id
byte idConst = 0x7F;
byte[] idArray = new byte[2];
//reading & constructing & adding articles to the list
int numBytesToRead = (int)fStream.Length;
while (numBytesToRead > 0)
{
byte[] original = new byte[32];
byte[] data = new byte[32];
int len = fStream.Read(original, 0, 32);
numBytesToRead -= 32;
if (len == 0 || len != 32)
{
MessageBox.Show("Error while loading articles");
break;
}
long pos = fStream.Position; //delete me
//decrypt
MemoryStream m = new MemoryStream();
using (Stream c = new CryptoStream(m, decryptor, CryptoStreamMode.Write))
c.Write(original, 0, original.Length);
data = m.ToArray();
//constructing object - article
//inUse
byte inUseCalc = (byte)(data[0] & inUseConst);
bool inUse = false;
if (inUseCalc != 0)
{
inUse = true;
}
//id
data[0] = (byte)(data[0] & idConst);
int id = (int)(BitConverter.ToUInt16(data, 0));
//stock
double stock = BitConverter.ToDouble(data, 2);
//name
String name = Encoding.UTF8.GetString(data, 10, 22);
Article article = new Article(id, 10, name, inUse, stock);
articles.Add(article);
Some things are not optimal, because I changed a lot just to try to find the solution. Some things (like converting to uInt16 and using 'or' and alike) are partially because of compression.
Please help me solve this and again please don't focus on my handling with the data or advising me to use serialization or binary writer or similar, I really have my reasons.
The actual problem is that you are re-creating the output CryptoStream each time. Apparently doing this is modifying some state in the encryptor which causes the first few bytes of each record to be output differently to how the decryptor is expecting it.
If you construct the encrypting CryptoStream outside of the loop, either writing directly to the output file or to a single MemoryStream, the problem goes away (and I actually tested it this time...)
using (var fStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read))
using (var m = new MemoryStream())
using (var c = new CryptoStream(m, encryptor, CryptoStreamMode.Write))
{
foreach (Article article in articles)
{
// ...
c.Write(data, 0, data.Length);
byte[] original = new byte[32];
original = m.ToArray();
m.Position = 0;
fStream.Write(original, 0, original.Length);
}
}
CryptoStream is used identically to any other stream, that is, if you wrap it around a FileStream and cast it back to Stream, you can't tell the difference.
In you decryption code, you are writing to the CryptoStream rather than reading from it. You probably want something more like this:
using (Stream c = new CryptoStream(fStream, decryptor, CryptoStreamMode.Read))
{
while (numBytesToRead > 0)
{
byte[] original = new byte[32];
byte[] data = new byte[32];
int len = c.Read(original, 0, 32);
numBytesToRead -= 32;
// and so on
}
}
(As requested, I'm not passing comment on your general approach. However, I would suggest that if possible you read the entire CryptoStream into memory (write it immediately into a MemoryStream) and close the original file and stream objects as soon as possible.)