Encryption Problems - Machine Key, Symmetrical Key, Rijndael, Encrypt/Decrypt across multiple machines
I recently created a SSO (Single Sign On) system. This envolved using the Cryptography Application Block 4.1 from Microsoft. There is a Web Application that talks to a backend WCF authentication service. Both typically sit on the same machine. The scope of this is that some user data would be encrypted within the database and is access through the WCF Authentication Service. This would be done on three different environments (Dev, QA, PROD). I need to be able from my Dev box connect to each WCF Authentication Service on any environment and encrypt and decrypt the data. I thought this would be a walk in the park. However this was not as simple as I thought it would be.
My Setup:
Developer Environment:
Windows 7 developer box
Visual Studio 2008
IIS 6
Enterprise Library 4.1 (Specifically using Cryptography Application Block)
QA Environment:
Windows Server 2003
IIS 6
PROD Environment:
Windows Server 2003
IIS 6
If not done correctly, you could come across the following errors:
Padding is invalid and cannot be removed
Stack Trace:
at
System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[]
inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]&
outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean
fLast) at
System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[]
inputBuffer, Int32 inputOffset, Int32 inputCount) at
System.Security.Cryptography.CryptoStream.FlushFinalBlock() at
System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean
fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length,
IVType ivType, Boolean useValidationSymAlgo) at
System.Web.UI.Page.DecryptStringWithIV(String s, IVType ivType) at
System.Web.UI.Page.DecryptString(String s) at
System.Web.Handlers.ScriptResourceHandler.DecryptParameter(NameValueCollection queryString)
Key not valid for use in specified state
System.InvalidOperationException: Key versions do not match between encrypted key and decryption algorithm
at
Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.KeyReaderWriter.Restore(Stream
protectedKeyStream, String passphrase)
at
Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.KeyReaderWriter.Restore(Stream
protectedKeyStream, String passphrase, DataProtectionScope
protectionScope)
at
Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.KeyManager.RestoreKey(Stream
inputStream, String passphrase, DataProtectionScope protectionScope)
at
Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.Configuration.Design.CryptographicKeyWizard.btnFinish_Click(Object
sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Enterprise Library Configuration - Export Key Wizard : An error occurred exporting the key
Unable to update the password. The value provided for the new password
does not meet the length, complexity, or history requirement of the
domain.
The Steps to Success to have you up and running on multiple environments:
Step 1. I use this code from Microsoft to generate a Machine Key How to create keys by using Visual C# .NET for use in Forms authentication
This would be the same across all environments. You need this if you want to decrypt across many machines.
Step 2. I use the generated MachineKey (using GenerateKey.exe 24 64) and place the generated MachineKey into the Web.Config of my Web Application and WCF Authentication Service
Step 3. Open Enterprise Library Config tool and
- Right click Enterprise Library Configuration > New Application
- Right click Application Configuration > New > Cryptography Application Block
- Right click Symmetric Providers > New > Symmetric Algorithm Provider
- (Type Selector - System.Security.Cryptography.SymmetricAlgorithm) : RijndaelManaged is by default selected, click OK
- (Cryptographic Key Wizard) : Create a new key is by default selected, click Next >
- (Cryptographic Key Wizard) : Click on Generate (auto populates text area), click Next >
- (Cryptographic Key Wizard) : Choose a file to export the key to: Example: Generated.Key
- (Cryptographic Key Wizard) : Please select the data protection mode : Choose Machine, click Finish
- Take the new created file Generated.key and copy it into my web application and WCF Authentication Service.
You will see that RijndaelManaged now appears under Symmetric Providers.
- Right Click RijndaelManaged > Export Key (This step is important. It appears there is a bug Entprise Library Config on trying to open the key later on and trying to export. You will end up getting the error: Enterprise Library Configuration - Export Key Wizard : An error occurred exporting the key)
- Enter a File name - This will take the key for the algorithm, encrypt it with a password and saved to a Text file
- Enter a "Password" and "Confirm Password". SAVE THE PASSWORD IN A SAFE PLACE
- Click OK
Now that you have created the Key Text file, there are two things you can do if needing to use the key on another machine:
-
You can take the exported Key Text File to another machine
- Copy Key text file to new machine
- Click on Start > All Programs > Microsoft Patterns and Practices > Enterprise Library 4.1 - October 2008 > Enterprise Library Config
- Click File > New Application
- Right Click Application Configuration > New > Cryptography Application Block
- Type Selector - RijndaelManaged (default), click OK
- Right click Symmetric Providers > New > Symmetric Algorithm Provider
- Cryptographic Key Wizard: Import a password-protected key file, Click Next
- Cryptographic Key Wizard: Select the originally created Text Key file and enter the password, click Next
- Cryptographic Key Wizard: Enter a filename to export the .Key file to, Click Next
- Cryptographic Key Wizard: Machine Mode, Click Next
- Cryptographic Key Wizard: Click Finished
- Click Save, this will create the Algorithm Key file
- Write a program to automatically read the Key Text file and create a new Symmetric Key specific to that machine.
- Here is a post
by DevLingo on automatically converting the Key Text file back into a Symmetric Key specific to that machine.
- Here is a C# version of the DevLingo VB code
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography;
namespace EnterpriseFramework.Security.Console.GenerateValidationKey
{
public class GenerateNewAlgorithmKey
{
//Private class members
private static string SECURITYKEYFILE = "XFileSecurity.key";
private static string KEYIMPORTFILE = "ColorIndex.txt";
private static string KEYIMPORTFILE_PW = "color2007";
public void GenerateNewKey()
{
RewriteAlgorithmKey("ColorIndex.txt", "color2007", "XFileSecurity.key");
}
private bool RewriteAlgorithmKey(string importKeyTextFile, string importKeyTextFilePassword, string keyExportFilename)
{
bool secure = false;
string sCurrentPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
do
{
sCurrentPath = sCurrentPath.Remove(sCurrentPath.Length - 1);
} while (sCurrentPath.EndsWith("\\"));
string importFile = sCurrentPath + importKeyTextFile;
string exportFile = sCurrentPath + keyExportFilename;
if (!System.IO.File.Exists(exportFile))
{
ProtectedKey key = KeyManager.RestoreKey(File.Open(importFile, FileMode.Open, FileAccess.Read), importKeyTextFilePassword, DataProtectionScope.LocalMachine);
KeyManager.Write(File.Open(exportFile, FileMode.Create), key);
}
Configuration cfg = System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (cfg.HasFile)
{
ConfigurationSection cryptoSection = cfg.Sections["securityCryptographyConfiguration"];
if (cryptoSection != null)
{
CryptographySettings cryptoSettings = cryptoSection as CryptographySettings;
SymmetricProviderData data = cryptoSettings.SymmetricCryptoProviders.Get(0);
string sSetPath = data.ElementInformation.Properties["protectedKeyFilename"].Value.ToString();
if (sSetPath != exportFile)
{
data.ElementInformation.Properties["protectedKeyFilename"].Value = exportFile;
cfg.Save(ConfigurationSaveMode.Minimal);
}
secure = true;
}
}
return secure;
}
}
}
Great Encoding/Decrypt example http://blogs.msdn.com/shawnfa/archive/2005/11/10/491431.aspx