C# Encrypt serialized file before writing to disk
Let's say my program has a class called "customer" and the customer class is serializable so I can read and write it to disk. The customer class holds sensitive information that I want to encrypt, the only way I know I could keep the file safe would be to:
1-Serialize the file to disk
2-Reopen and load the file
3-Encrypt the file
4-Rewrite file to disk
This would work, but there is a risk that the file could be intercepted in it's unencrypted state and furthermore this is just really inefficient.
Instead I would like to:
1-Create file in memory
2-Encrypt file in memory
3-Write encrypted file to disk
Is this possible?开发者_开发问答 If it is how? Thanks in advance.
You can use a CryptoStream to do the encryption at the same time as you serialize the class to a file:
byte[] key = { 1, 2, 3, 4, 5, 6, 7, 8 }; // Where to store these keys is the tricky part,
// you may need to obfuscate them or get the user to input a password each time
byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 };
string path = @"C:\path\to.file";
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
// Encryption
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
using (var cryptoStream = new CryptoStream(fs, des.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
BinaryFormatter formatter = new BinaryFormatter();
// This is where you serialize the class
formatter.Serialize(cryptoStream, customClass);
}
// Decryption
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var cryptoStream = new CryptoStream(fs, des.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
BinaryFormatter formatter = new BinaryFormatter();
// This is where you deserialize the class
CustomClass deserialized = (CustomClass)formatter.Deserialize(cryptoStream);
}
Aside from the concerns expressed in the comments, if all you are asking is how to work with the bytes in memory and only write them to the file once, just serialize your object to a memory stream first. Encrypt those bytes and write them to the file.
using (var fileStream = File.OpenWrite(theFileName))
using (var memoryStream = new MemoryStream())
{
// Serialize to memory instead of to file
var formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, customer);
// This resets the memory stream position for the following read operation
memoryStream.Seek(0, SeekOrigin.Begin);
// Get the bytes
var bytes = new byte[memoryStream.Length];
memoryStream.Read(bytes, 0, (int)memoryStream.Length);
// Encrypt your bytes with your chosen encryption method, and write the result instead of the source bytes
var encryptedBytes = yourCrypto.Encrypt(bytes);
fileStream.Write(encryptedBytes, 0, encryptedBytes.Length);
}
Its quite possible,
Lets say you class looks like
public class Customer
{
public string Name{get;set;}
public int salary {get;set;}
}
You could encrypt the data held in properties of the object so customer.Name = 'ABC' could become customer.Name = 'WQW' something like that
than serialize it.
On deserialization, when you have to show the data you have to decrypt it before showing the data to the user
hope this help
I would create a class for serialization which offers a property. reading it (giving a filename) returns the deserialized objects, writing to it (also giving a filename) serializes the object. I added a second property with a String password. When using it you can encrypt the serialized object string and the nwrite to disk or when reading from it 1. encrypt, then deserialize.
To encrypt, I would recommend a hash function from the password instead of directly using it. Unfortunately I only have a code example in vb.net:
Function Encrypt(ByVal data As String, ByVal password As String) As String
Dim pdb As New Rfc2898DeriveBytes(password, Salt)
Dim alg As Rijndael = Rijndael.Create()
alg.Key = pdb.GetBytes(32)
alg.IV = pdb.GetBytes(16)
Dim ms As New IO.MemoryStream
Dim cs As New CryptoStream(ms, alg.CreateEncryptor, CryptoStreamMode.Write)
cs.Write(System.Text.Encoding.Default.GetBytes(data), 0, data.Length)
cs.Close()
ms.Close()
Return Convert.ToBase64String(ms.ToArray)
End Function
Private Salt As Byte() = {100, 86, 34, 53, 11, 224, 145, 123, _
237, 213, 12, 124, 45, 65, 71, 127, _
135, 165, 234, 164, 127, 234, 231, 211, _
10, 9, 114, 234, 44, 63, 75, 12}
Function Decrypt(ByVal data As String, ByVal password As String) As String
Dim pdb As New Rfc2898DeriveBytes(password, Salt)
Dim alg As Rijndael = Rijndael.Create()
alg.Key = pdb.GetBytes(32)
alg.IV = pdb.GetBytes(16)
Dim ms As New IO.MemoryStream
Dim cs As New CryptoStream(ms, alg.CreateDecryptor, CryptoStreamMode.Write)
cs.Write(Convert.FromBase64String(data), 0, Convert.FromBase64String(data).Length)
cs.Close()
ms.Close()
Return System.Text.Encoding.Default.GetString(ms.ToArray)
End Function
and
Function EncryptWithHash(ByVal data As String, ByVal passToHash As String) As String
Dim _hash As String = getMd5Hash(passToHash)
Dim _result As String = Encrypt(data, _hash)
Return _result
End Function
Function DecryptWithHash(ByVal data As String, ByVal passToHash As String) As String
Dim _hash As String = getMd5Hash(passToHash)
Dim _result As String = Encrypt(data, _hash)
Return _result
End Function
Function getMd5Hash(ByVal input As String) As String
' Create a new instance of the MD5CryptoServiceProvider object.
Dim md5Hasher As New MD5CryptoServiceProvider()
' Convert the input string to a byte array and compute the hash.
Dim data As Byte() = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input))
' Create a new Stringbuilder to collect the bytes
' and create a string.
Dim sBuilder As New StringBuilder()
' Loop through each byte of the hashed data
' and format each one as a hexadecimal string.
Dim i As Integer
For i = 0 To data.Length - 1
sBuilder.Append(data(i).ToString("x2"))
Next i
' Return the hexadecimal string.
Return sBuilder.ToString()
End Function
the property in my code has Get:
Dim _dataC As String = ReadFile(filename)
Dim _dataR As String = Crypt.Decrypt(_dataC, password)
Dim _result = tmpS.ReadString(_dataR)
and Set:
Dim _tmpS As New Custom.Serialization(Of Object)
Dim _tmpRaw As String = _tmpS.WriteString(value)
Dim _tmpCrypt As String = Crypt.Encrypt(_tmpRaw, password)
WriteFile(tmpPath, _tmpCrypt)
you will have to define your own Serialization, and to read/write Files:
My.Computer.FileSystem.WriteAllText(filename, data, False)
_result = My.Computer.FileSystem.ReadAllText(FileName)
精彩评论