I have a client-server architecture in my application that uses SSL. Currently, the private key is stored in CAPI's key store location. For security reasons, I'd like to store the key in a safer place, ideally a hardware signing module (HSM) that is built for this purpose. Unfortunately, with the private key stored on such a device, I can't figure out how to use it in my application.
On the server, I am simply using the SslStream
class and the AuthenticateAsServer(...)
call. This method takes an X509Certificate
object that has its private key loaded, but since the private key is stored in a secure (e.g. non exportable) location on the HSM, I don't know how to do this.
On the client, I am using an HttpWebRequest
object and then using the ClientCertificates
property to add my client authentication certificate, but I have the same problem here: how do I get the private key?
I know there are some HSMs that act as SSL accelerators but I don't really need an accelerator. Also, these products tend to have special integration with web servers such as IIS and Apache which I'm not using.
Any ideas? The only thing I can think of would be to write my own SSL library that would allow me to hand off the signing portion of the transaction to the HSM, but this seems like a huge amount of work.
As Rasmus stated you should use CSP from your HSM producer. Check this link:
http://forums.asp.net/t/1531893.aspx
I successfully used a little bit different approach on the client for client-authenticated HTTPS with HttpWebRequest
, ClientCertificates
and smart card. In my case private key is stored in the smart card (similar to HSM). Smart card CSP then uses PKCS#11 for signing, enc/decrypt etc, but that is not important. Property X509Certificate.Handle
is used in SSL establishment for signing the challenge on the client and this handle contains information about certificate's private key.
In my case I wanted to set pin programatically for smart card to avoid "Enter PIN" dialog from smart card in process of SSL creation, and I've done it with this function:
public void SetContext(X509Certificate2 cert)
{
IntPtr p = IntPtr.Zero;
bool result = Win32.CryptAcquireContext(ref p, "keyContainer", "Siemens Card API CSP", 1, 0);
byte[] pin = new ASCIIEncoding().GetBytes("0000");
result = Win32.CryptSetProvParam(p, 32, pin, 0);
result = Win32.CertSetCertificateContextProperty(cert.Handle, 1, 0, p);
}
You can find names of all installed CSPs in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
. Win32
is my class for C++/C# interop and looks like this:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptAcquireContext(
ref IntPtr hProv,
string pszContainer,
string pszProvider,
uint dwProvType,
uint dwFlags
);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CryptSetProvParam(
IntPtr hProv,
uint dwParam,
[In] byte[] pbData,
uint dwFlags);
[DllImport("CRYPT32.DLL")]
internal static extern Boolean CertSetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
uint dwFlags,
IntPtr pvData
);