You have sensitive information that must be encrypted before it is written to a file that might be in a nonsecure area. This information must also be decrypted before it is read back in to the application.
Use multiple
cryptography providers and write the
data to a file in encrypted format. This is accomplished in the
following class, whose constructor expects an instance of the
System.Security.Cryptography.SymmetricAlgorithm
class and a path for the file. The
SymmetricAlgorithm
class is an abstract base class
for all cryptographic providers in .NET, so we can be reasonably
assured that this class could be extended to cover all of them. This
example implements support for TripleDES and Rijndael. It could
easily be extended for DES and RC2, which are also provided by the
framework.
The following namespaces are needed for this solution:
using System; using System.Text; using System.IO; using System.Security.Cryptography;
The
class SecretFile
can be used for TripleDES as
shown:
// Use TripleDES TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider( ); SecretFile secretTDESFile = new SecretFile(tdes,"tdestext.secret"); string encrypt = "My TDES Secret Data!"; Console.WriteLine("Writing secret data: {0}",encrypt); secretTDESFile.SaveSensitiveData(encrypt); // save for storage to read file byte [] key = secretTDESFile.Key; byte [] IV = secretTDESFile.IV; string decrypt = secretTDESFile.ReadSensitiveData( ); Console.WriteLine("Read secret data: {0}",decrypt); // release resources tdes.Clear( );
To use
SecretFile
with Rijndael, just substitute the
provider in the constructor like this:
// Use Rijndael RijndaelManaged rdProvider = new RijndaelManaged( ); SecretFile secretRDFile = new SecretFile(rdProvider,"rdtext.secret"); string encrypt = "My Rijndael Secret Data!"; Console.WriteLine("Writing secret data: {0}",encrypt); secretRDFile.SaveSensitiveData(encrypt); // save for storage to read file byte [] key = secretRDFile.Key; byte [] IV = secretRDFile.IV; string decrypt = secretRDFile.ReadSensitiveData( ); Console.WriteLine("Read secret data: {0}",decrypt); // release resources rdProvider.Clear( );
Here is the
implementation of SecretFile
:
public class SecretFile { private byte[] savedKey = null; private byte[] savedIV = null; private SymmetricAlgorithm symmetricAlgorithm; string path; public byte[] Key { get { return savedKey; } set { savedKey = value; } } public byte[] IV { get { return savedIV; } set { savedIV = value; } } public SecretFile(SymmetricAlgorithm algorithm, string fileName) { symmetricAlgorithm = algorithm; path = fileName; } public void SaveSensitiveData(string sensitiveData) { // Encode data string to be stored in encrypted file byte[] encodedData = Encoding.Unicode.GetBytes(sensitiveData); // Create FileStream and crypto service provider objects FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write); // Generate and save secret key and init vector GenerateSecretKey( ); GenerateSecretInitVector( ); // Create crypto transform and stream objects ICryptoTransform transform = symmetricAlgorithm.CreateEncryptor(savedKey, savedIV); CryptoStream cryptoStream = new CryptoStream(fileStream, transform, CryptoStreamMode.Write); // Write encrypted data to the file cryptoStream.Write(encodedData, 0, encodedData.Length); // Release all resources cryptoStream.Close( ); transform.Dispose( ); fileStream.Close( ); } public string ReadSensitiveData( ) { // Create file stream to read encrypted file back FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); //print out the contents of the encrypted file BinaryReader binReader = new BinaryReader(fileStream); Console.WriteLine("---------- Encrypted Data ---------"); int count = (Convert.ToInt32(binReader.BaseStream.Length)); byte [] bytes = binReader.ReadBytes(count); char [] array = Encoding.Unicode.GetChars(bytes); string encdata = new string(array); Console.WriteLine(encdata); Console.WriteLine("---------- Encrypted Data --------- "); // reset the file stream fileStream.Seek(0,SeekOrigin.Begin); // Create Decryptor ICryptoTransform transform = symmetricAlgorithm.CreateDecryptor(savedKey, savedIV); CryptoStream cryptoStream = new CryptoStream(fileStream, transform, CryptoStreamMode.Read); //print out the contents of the decrypted file StreamReader srDecrypted = new StreamReader(cryptoStream, new UnicodeEncoding( )); Console.WriteLine("---------- Decrypted Data ---------"); string decrypted = srDecrypted.ReadToEnd( ); Console.WriteLine(decrypted); Console.WriteLine("---------- Decrypted Data ---------"); // Release all resources binReader.Close( ); srDecrypted.Close( ); cryptoStream.Close( ); transform.Dispose( ); fileStream.Close( ); return decrypted; } private void GenerateSecretKey( ) { if(null != (symmetricAlgorithm as TripleDESCryptoServiceProvider)) { TripleDESCryptoServiceProvider tdes; tdes = symmetricAlgorithm as TripleDESCryptoServiceProvider; tdes.KeySize = 192; // Maximum key size tdes.GenerateKey( ); savedKey = tdes.Key; } else if(null != (symmetricAlgorithm as RijndaelManaged)) { RijndaelManaged rdProvider; rdProvider = symmetricAlgorithm as RijndaelManaged; rdProvider.KeySize = 256; // Maximum key size rdProvider.GenerateKey( ); savedKey = rdProvider.Key; } } private void GenerateSecretInitVector( ) { if(null != (symmetricAlgorithm as TripleDESCryptoServiceProvider)) { TripleDESCryptoServiceProvider tdes; tdes = symmetricAlgorithm as TripleDESCryptoServiceProvider; tdes.GenerateIV( ); savedIV = tdes.IV; } else if(null != (symmetricAlgorithm as RijndaelManaged)) { RijndaelManaged rdProvider; rdProvider = symmetricAlgorithm as RijndaelManaged; rdProvider.GenerateIV( ); savedIV = rdProvider.IV; } } }
If the
SaveSensitiveData
method is used to save the
following text to a file:
This is a test This is sensitive data!
the
ReadSensitiveData
method will display the
following information from this same file:
---------- Encrypted Data --------- ???????????????????????????????????????? ---------- Encrypted Data --------- ---------- Decrypted Data --------- This is a test This is sensitive data! ---------- Decrypted Data ---------
Encrypting data is essential to many applications, especially ones that store information in easily accessible locations. Once data is encrypted, a decryption scheme is required to restore the data back to an unencrypted form without losing any information. The same underlying algorithms can be used to authenticate the source of a file or message.
The encryption schemes used in this recipe are TripleDES and Rijndael. The reason for using Triple DES are:
TripleDES employs symmetric encryption, meaning that a single private key is used to encrypt and decrypt data. This process allows much faster encryption and decryption, especially as the streams of data become larger.
TripleDES encryption is much harder to crack than the older DES encryption.
If you wish to use another type of encryption, this recipe can be
easily converted using any provider derived from the
SymmetricAlgorithm
class.
The main drawback to TripleDES is that both the sender and receiver must use the same key and Initialization Vector (IV) in order to encrypt and decrypt the data successfully. If you wish to have an even more secure encryption scheme, use the Rijndael scheme. This type of encryption scheme is highly regarded as a solid encryption scheme, since it is fast and can use larger key sizes than TripleDES. However, it is still a symmetric cryptosystem, which means that it relies on shared secrets. Use an asymmetric cryptosystem, such as RSA or DSA, for a cryptosystem that uses shared public keys with private keys that are never shared between parties.