DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 14.3 Encrypting and Decrypting a File

Problem

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.

Solution

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 ---------\r\n");
        
        // 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 ---------

Discussion

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.

See Also

See the "SymmetricAlgorithm Class," "TripleDESCryptoServiceProvider Class," and "RijndaelManaged Class" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section