views:

40

answers:

2

I have some data I want to encrypt in an ASP.NET MVC application to prevent users from tampering with it. I can use the Cryptography classes to do the actual encryption/decryption, no problem there. The main problem is figuring out where to store the encryption key and managing changes to it.

Since ASP.NET already maintains a machineKey for various things (ViewData encryption, etc), I was wondering if there were any ASP.NET functions which let me encrypt/decrypt my own data using the machineKey? This way I would not have to devise my own key management system.

A: 

I guess not directly. I can't remember where I got this from, probably a combination of Reflector and some blogs.

public abstract class MyAwesomeClass
{
    private static byte[] cryptKey;

    private static MachineKeySection machineKeyConfig =
        (MachineKeySection)ConfigurationManager
            .GetSection("system.web/machineKey");

    // ... snip ...

    static MyAwesomeClass()
    {
        string configKey;
        byte[] key;

        configKey = machineKeyConfig.DecryptionKey;
        if (configKey.Contains("AutoGenerate"))
        {
            throw new ConfigurationErrorsException(
                Resources.MyAwesomeClass_ExplicitAlgorithmRequired);
        }

        key = HexStringToByteArray(configKey);

        cryptKey = key;
    }

    // ... snip ...

    protected static byte[] Encrypt(byte[] inputBuffer)
    {
        SymmetricAlgorithm algorithm;
        byte[] outputBuffer;

        if (inputBuffer == null)
        {
            throw new ArgumentNullException("inputBuffer");
        }

        algorithm = GetCryptAlgorithm();

        using (var ms = new MemoryStream())
        {
            algorithm.GenerateIV();
            ms.Write(algorithm.IV, 0, algorithm.IV.Length);

            using (var cs = new CryptoStream(
                 ms, 
                 algorithm.CreateEncryptor(), 
                 CryptoStreamMode.Write))
            {
                cs.Write(inputBuffer, 0, inputBuffer.Length);
                cs.FlushFinalBlock();
            }

            outputBuffer = ms.ToArray();
        }

        return outputBuffer;
    }

    protected static byte[] Decrypt(string input)
    {
        SymmetricAlgorithm algorithm;
        byte[] inputBuffer, inputVectorBuffer, outputBuffer;

        if (input == null)
        {
            throw new ArgumentNullException("input");
        }

        algorithm = GetCryptAlgorithm();
        outputBuffer = null;

        try
        {
            inputBuffer = Convert.FromBase64String(input);

            inputVectorBuffer = new byte[algorithm.IV.Length];
            Array.Copy(
                 inputBuffer, 
                 inputVectorBuffer,
                 inputVectorBuffer.Length);
            algorithm.IV = inputVectorBuffer;

            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(
                    ms, 
                    algorithm.CreateDecryptor(), 
                    CryptoStreamMode.Write))
                {
                    cs.Write(
                        inputBuffer,
                        inputVectorBuffer.Length, 
                        inputBuffer.Length - inputVectorBuffer.Length);
                    cs.FlushFinalBlock();
                }

                outputBuffer = ms.ToArray();
            }
        }
        catch (FormatException e)
        {
            throw new CryptographicException(
                "The string could not be decoded.", e);
        }

        return outputBuffer;
    }

    // ... snip ...

    private static SymmetricAlgorithm GetCryptAlgorithm()
    {
        SymmetricAlgorithm algorithm;
        string algorithmName;

        algorithmName = machineKeyConfig.Decryption;
        if (algorithmName == "Auto")
        {
            throw new ConfigurationErrorsException(
                Resources.MyAwesomeClass_ExplicitAlgorithmRequired);
        }

        switch (algorithmName)
        {
            case "AES":
                algorithm = new RijndaelManaged();
                break;
            case "3DES":
                algorithm = new TripleDESCryptoServiceProvider();
                break;
            case "DES":
                algorithm = new DESCryptoServiceProvider();
                break;
            default:
                throw new ConfigurationErrorsException(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        Resources.MyAwesomeClass_UnrecognizedAlgorithmName,
                        algorithmName));
        }

        algorithm.Key = cryptKey;

        return algorithm;
    }

    private static byte[] HexStringToByteArray(string str)
    {
        byte[] buffer;

        if (str == null)
        {
            throw new ArgumentNullException("str");
        }

        if (str.Length % 2 == 1)
        {
            str = '0' + str;
        }

        buffer = new byte[str.Length / 2];

        for (int i = 0; i < buffer.Length; ++i)
        {
            buffer[i] = byte.Parse(
                str.Substring(i * 2, 2),
                NumberStyles.HexNumber,
                CultureInfo.InvariantCulture);
        }

        return buffer;
    }
}

Caveat emptor!

Nicholas Piasecki
A: 

You might be able to reuse the MembershipProvider.EncryptPassword method, which in turn uses some (unfortunately internal) encryption methods of the MachineKeySection class.

cxfx