Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save brendanmckenzie/1373fed141f7dcd808b08c58aa1a56c4 to your computer and use it in GitHub Desktop.
Save brendanmckenzie/1373fed141f7dcd808b08c58aa1a56c4 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
namespace MyApp.Core.Security
{
/// <summary>
/// Class Pkcs12ProtectedConfigurationProvider.
/// </summary>
/// <seealso cref="ProtectedConfigurationProvider" />
public class Pkcs12ProtectedConfigurationProvider : ProtectedConfigurationProvider
{
private string thumbprint;
private StoreLocation storeLocation;
/// <summary>
/// Decrypts the XML node passed to it.
/// </summary>
/// <param name="encryptedNode">The XmlNode to decrypt.</param>
/// <returns></returns>
public override XmlNode Decrypt(XmlNode encryptedNode)
{
var document = new XmlDocument();
// Get the RSA private key. This key will decrypt
// a symmetric key that was embedded in the XML document.
var cryptoServiceProvider = GetCryptoServiceProvider(false);
document.PreserveWhitespace = true;
document.LoadXml(encryptedNode.OuterXml);
var xml = new EncryptedXml(document);
// Add a key-name mapping.This method can only decrypt documents
// that present the specified key name.
xml.AddKeyNameMapping("rsaKey", cryptoServiceProvider);
xml.DecryptDocument();
cryptoServiceProvider.Clear();
return document.DocumentElement;
}
/// <summary>
/// Encrypts the XML node passed to it.
/// </summary>
/// <param name="node">The XmlNode to encrypt.</param>
/// <returns></returns>
public override XmlNode Encrypt(XmlNode node)
{
// Get the RSA public key to encrypt the node. This key will encrypt
// a symmetric key, which will then be encryped in the XML document.
var cryptoServiceProvider = GetCryptoServiceProvider(true);
// Create an XML document and load the node to be encrypted in it.
var document = new XmlDocument();
document.PreserveWhitespace = true;
document.LoadXml("<Data>" + node.OuterXml + "</Data>");
// Create a new instance of the EncryptedXml class
// and use it to encrypt the XmlElement with the
// a new random symmetric key.
var xml = new EncryptedXml(document);
var documentElement = document.DocumentElement;
SymmetricAlgorithm symmetricAlgorithm = new RijndaelManaged
{
// Create a 192 bit random key.
Key = GetRandomKey()
};
symmetricAlgorithm.GenerateIV();
symmetricAlgorithm.Padding = PaddingMode.PKCS7;
var buffer = xml.EncryptData(documentElement, symmetricAlgorithm, true);
// Construct an EncryptedData object and populate
// it with the encryption information.
var encryptedData = new EncryptedData
{
Type = EncryptedXml.XmlEncElementUrl,
// Create an EncryptionMethod element so that the
// receiver knows which algorithm to use for decryption.
EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES192Url),
KeyInfo = new KeyInfo()
};
// Encrypt the session key and add it to an EncryptedKey element.
var encryptedKey = new EncryptedKey
{
EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url),
KeyInfo = new KeyInfo(),
CipherData = new CipherData
{
CipherValue = EncryptedXml.EncryptKey(symmetricAlgorithm.Key, cryptoServiceProvider, false)
}
};
var clause = new KeyInfoName
{
Value = "rsaKey"
};
// Add the encrypted key to the EncryptedData object.
encryptedKey.KeyInfo.AddClause(clause);
var key2 = new KeyInfoEncryptedKey(encryptedKey);
encryptedData.KeyInfo.AddClause(key2);
encryptedData.CipherData = new CipherData
{
CipherValue = buffer
};
// Replace the element from the original XmlDocument
// object with the EncryptedData element.
EncryptedXml.ReplaceElement(documentElement, encryptedData, true);
foreach (XmlNode node2 in document.ChildNodes)
{
if (node2.NodeType == XmlNodeType.Element)
{
foreach (XmlNode node3 in node2.ChildNodes)
{
if (node3.NodeType == XmlNodeType.Element)
{
return node3;
}
}
}
}
return null;
}
/// <summary>
/// Initializes the provider with default settings.
/// </summary>
/// <param name="name"></param>
/// <param name="configurationValues">A NameValueCollection collection of values to use
/// when initializing the object. This must include a thumbprint value for the thumbprint of
/// the certificate used to encrypt the configuration section.
/// </param>
public override void Initialize(string name, NameValueCollection configurationValues)
{
base.Initialize(name, configurationValues);
if (configurationValues["thumbprint"] == null || configurationValues["thumbprint"].Length == 0)
{
throw new ApplicationException("thumbprint not set in the configuration");
}
this.thumbprint = configurationValues["thumbprint"];
try
{
this.storeLocation = (StoreLocation)Enum.Parse(typeof(StoreLocation), configurationValues["storeLocation"]);
}
catch (Exception ex)
{
throw new Exception(@"storeLocation must be ""LocalMachine"" or ""CurrentUser"".", ex);
}
}
/// <summary>
/// Get certificate from the Local Machine store, based on the given thumbprint
/// </summary>
/// <param name="thumbprint">The thumbnail of the certificate used to encrypt the configuration file.</param>
/// <param name="storeLocation">This can be CurrentUser or LocalMachine. Generally speaking Azure uses CurrentUser and Windows IIS uses LocalMachine.</param>
/// <returns></returns>
/// <remarks>The certificate, with private key, must be accessible in the indicated store location.</remarks>
private X509Certificate2 GetCertificate(string thumbprint, StoreLocation storeLocation)
{
var store = new X509Store(StoreName.My, storeLocation);
X509Certificate2Collection certificates = null;
store.Open(OpenFlags.ReadOnly);
try
{
X509Certificate2 result = null;
certificates = store.Certificates;
for (var i = 0; i < certificates.Count; i++)
{
var cert = certificates[i];
if (cert.Thumbprint.ToLower().CompareTo(thumbprint.ToLower()) == 0)
{
result = new X509Certificate2(cert);
return result;
}
}
if (result == null)
{
throw new ApplicationException(string.Format("No certificate was found for thumbprint {0}", thumbprint));
}
return null;
}
finally
{
if (certificates != null)
{
for (var i = 0; i < certificates.Count; i++)
{
var cert = certificates[i];
cert.Reset();
}
}
store.Close();
}
}
/// <summary>
/// Get either the public key for encrypting configuration sections or the private key to decrypt them.
/// </summary>
/// <param name="IsEncryption"></param>
/// <returns></returns>
private RSACryptoServiceProvider GetCryptoServiceProvider(bool IsEncryption)
{
RSACryptoServiceProvider provider;
var cert = GetCertificate(this.thumbprint, this.storeLocation);
if (IsEncryption)
{
provider = (RSACryptoServiceProvider)cert.PublicKey.Key;
}
else
{
provider = (RSACryptoServiceProvider)cert.PrivateKey;
}
return provider;
}
private byte[] GetRandomKey()
{
var data = new byte[0x18];
new RNGCryptoServiceProvider().GetBytes(data);
return data;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment