Reputation: 123
My organization requires that sensitive connection strings be encrypted using a particular System.Configuration.RsaProtectedConfigurationProvider located on our production servers. However, I have an application which stores connection strings in a database and I would like to perform encryption on these with the same keys.
My original thought was to create a dummy configuration file containing a reference to the encryption provider, load it with some text and perform the encryption without having to write it back to disk. I could then pull the cipher/plaintext out of the xml:
DummyConfig.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="DummySection" type="virutalConfig.DummySect, virutalConfig, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</configSections>
<DummySection />
<configProtectedData>
<providers>
<add name="MyProvider"
type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0,
 Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a,
 processorArchitecture=MSIL"
keyContainerName="MyKeys"
useMachineContainer="true" />
</providers>
</configProtectedData>
</configuration>
Program.cs:
class Program
{
static void Main(string[] args)
{
var fileMap = new ExeConfigurationFileMap
{
ExeConfigFilename = @"c:\virtconfigtest\DummyConfig.config"
};
var config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
var sect = config.GetSection("DummySection") as DummySect;
sect.Inf = "this is some plaintext!";
sect.SectionInformation.ProtectSection("MyProvider");
//sect.SectionInformation.ForceSave = true;
//config.Save();
if (sect.SectionInformation.IsProtected)
{
Console.WriteLine("Section is protected. Raw XML:");
Console.WriteLine(sect.SectionInformation.GetRawXml());
}
else
{
Console.WriteLine("Section is not protected. Raw XML:");
Console.WriteLine(sect.SectionInformation.GetRawXml());
}
Console.ReadLine();
}
}
public class DummySect : ConfigurationSection
{
public DummySect() { }
[ConfigurationProperty("inf")]
public string Inf
{
get { return (string)this["inf"]; }
set { this["inf"] = value; }
}
}
Unfortunately, GetRawXml() only returns the plaintext, and even to get this, it seems the config file must be written back to disk.
I could get what I want by reading the file off the disk as an xml document, but I'd rather not have to do that. The whole plan is pretty gross anyway, even without adding disk writes into the mix. Will I have to retrieve the RSA keys from the provider to do this? If so, how?
Upvotes: 1
Views: 1499
Reputation: 123
With some research and trial and error, I did eventually figure out how to do this.
Here's what the encrypted part of a config file looks like:
<DummySection configProtectionProvider="MyProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>RsMpDD/wJmmpN+Mme+qFuRVm2Ddk759hWM7HaeAnW7xpfkCoC4ko7vDBmqylzQ0QAFL2wuR8u8Bsf+4xwn++Ru/GsEaYrGrcDMYJTuWElyHuxnw+5umqexQJye2R5uL/91alFVNV41HnSPlwuA+pgk14yHSWIflIyKFmUTx58vU=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>lQI7gyQZ2HIIQUdKsp73HrYcebbOiO4dCriwCt5avfVTcxPZEHzaCfV52k+triRwq64uGVCNRpGUe5PCVEfbWwrPHaNaFzRp</CipherValue>
</CipherData>
</EncryptedData>
</DummySection>
The first block of cipher text is a triple DES key ecrypted with RSA. The keypair can be obtained from a file found in the directory: C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
The second part is encrypted with this DES key and prepended with a 64 bit initialization vector. Frustratingly, it is padded with the method: ISO10126
Here is the code to do the decryption:
var cspParameters = new CspParameters()
{
KeyContainerName = "MyKeys",
Flags = CspProviderFlags.UseMachineKeyStore
}; //refers to a file in the machine keys directory
var rsaKey = new RSACryptoServiceProvider(cspParameters);
var t1 =
Convert.FromBase64String(
"RsMpDD/wJmmpN+Mme+qFuRVm2Ddk759hWM7HaeAnW7xpfkCoC4ko7vDBmqylzQ0QAFL2wuR8u8Bsf+4xwn++Ru/GsEaYrGrcDMYJTuWElyHuxnw+5umqexQJye2R5uL/91alFVNV41HnSPlwuA+pgk14yHSWIflIyKFmUTx58vU=");
var t2 =
Convert.FromBase64String(
"lQI7gyQZ2HIIQUdKsp73HrYcebbOiO4dCriwCt5avfVTcxPZEHzaCfV52k+triRwq64uGVCNRpGUe5PCVEfbWwrPHaNaFzRp");
var desKey = rsaKey.Decrypt(t1, false); //get the des key
var iv = t2.Take(8).ToArray(); //get the initialization vector
var ct = t2.Skip(8).ToArray(); //get the actual ciphertext
var desEnc = new TripleDESCryptoServiceProvider()
{
Padding = PaddingMode.ISO10126
};
var plaintext = Encoding.Default.GetString(desEnc.CreateDecryptor(desKey, iv).TransformFinalBlock(ct, 0, ct.Length));
Upvotes: 1