We have a great X509 story in Whidbey as revealed in Beta1 there is now support for Certificate Stores, Extensions, Chain building and a more robust X509Certificate2 class. On the subject of chain building, quite often you will need to install Certificate Trust Lists (CTLs) and Certificate Revocation Lists (CRLs) for proper verification.
CRL and CTL support isn't in Whidbey and I've found very few resources for .Net sample code to be able to use these, so below is some sample code for installing a CRL and CTL into a certificate store. I'm using the X509Store class to gain access to a store - in Beta2 we added a StoreHandle property for this very purpose. I need the store context to be able to add various objects to the store.
The CRLContext and CTLContext classes are very similar. The constructors take a file and call CryptQueryObject to obtain a context to your desired object. The context is then stored in the hContext field of each class. A method AddToStore is then exposed which will add the object to the store - in the future I will talk about how to enumerate and remove these objects from the store.
using System;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
public class X509Sample
{
public static X509Store OpenStore(string StoreName, StoreLocation StoreLocation, OpenFlags Flags)
{
X509Store store = new X509Store(StoreName, StoreLocation);
store.Open(Flags);
return store;
}
}
public class CTLContext
{
IntPtr hContext;
public CTLContext(IntPtr Context)
{
hContext = CertDuplicateCTLContext(Context);
}
public unsafe CTLContext(string CTLFile)
{
IntPtr pContext = new IntPtr(Constants.CERT_QUERY_CONTENT_CTL);
GCHandle handle = GCHandle.Alloc(CTLFile, GCHandleType.Pinned);
IntPtr pbData = handle.AddrOfPinnedObject();
bool ret = CryptoAPI.CryptQueryObject(
Constants.CERT_QUERY_OBJECT_FILE,
pbData,
Constants.CERT_QUERY_CONTENT_FLAG_CTL,
Constants.CERT_QUERY_FORMAT_FLAG_ALL,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
new IntPtr(&pContext));
hContext = CertDuplicateCTLContext(pContext);
CertFreeCTLContext(pContext);
}
public bool AddToStore(X509Store Store, uint AddDisposition, ref IntPtr StoreContext)
{
return CertAddCTLContextToStore(Store.StoreHandle, this.hContext, AddDisposition, StoreContext);
}
~CTLContext()
{
if (hContext != IntPtr.Zero)
CertFreeCTLContext(hContext);
}
[DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr CertDuplicateCTLContext(IntPtr pCrlContext);
[DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool CertFreeCTLContext(IntPtr pCrlContext);
[DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool CertAddCTLContextToStore(
[In] IntPtr hCertStore,
[In] IntPtr pCrlContext,
[In] uint dwAddDisposition,
[Out] IntPtr ppStoreContext);
}
public class CRLContext
{
IntPtr hContext;
public CRLContext(IntPtr Context)
{
hContext = CertDuplicateCRLContext(Context);
}
public unsafe CRLContext(string CRLFile)
{
IntPtr pContext = new IntPtr(Constants.CERT_QUERY_CONTENT_CRL);
GCHandle handle = GCHandle.Alloc(CRLFile, GCHandleType.Pinned);
IntPtr pbData = handle.AddrOfPinnedObject();
bool ret = CryptoAPI.CryptQueryObject(
Constants.CERT_QUERY_OBJECT_FILE,
pbData,
Constants.CERT_QUERY_CONTENT_FLAG_CRL,
Constants.CERT_QUERY_FORMAT_FLAG_ALL,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
new IntPtr(&pContext));
hContext = CertDuplicateCRLContext(pContext);
CertFreeCRLContext(pContext);
}
public bool AddToStore(X509Store Store, uint AddDisposition, ref IntPtr StoreContext)
{
return CertAddCRLContextToStore(Store.StoreHandle, this.hContext, AddDisposition, StoreContext);
}
~CRLContext()
{
if (hContext != IntPtr.Zero)
CertFreeCRLContext(hContext);
}
[DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr CertDuplicateCRLContext(IntPtr pCrlContext);
[DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool CertFreeCRLContext(IntPtr pCrlContext);
[DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool CertAddCRLContextToStore(
[In] IntPtr hCertStore,
[In] IntPtr pCrlContext,
[In] uint dwAddDisposition,
[Out] IntPtr ppStoreContext);
}
internal static class CryptoAPI
{
[DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool CryptQueryObject(
[In] uint dwObjectType,
[In] IntPtr pvObject,
[In] uint dwExpectedContentTypeFlags,
[In] uint dwExpectedFormatTypeFlags,
[In] uint dwFlags,
[Out] IntPtr pdwMsgAndCertEncodingType,
[Out] IntPtr pdwContentType,
[Out] IntPtr pdwFormatType,
[In, Out] IntPtr phCertStore,
[In, Out] IntPtr phMsg,
[In, Out] IntPtr ppvContext);
}
internal static class Constants
{
//Store constants
internal const uint CERT_STORE_ADD_NEW = 1;
internal const uint CERT_STORE_ADD_USE_EXISTING = 2;
internal const uint CERT_STORE_ADD_REPLACE_EXISTING = 3;
internal const uint CERT_STORE_ADD_ALWAYS = 4;
internal const uint CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES = 5;
internal const uint CERT_STORE_ADD_NEWER = 6;
internal const uint CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES = 7;
internal const uint CERT_QUERY_CONTENT_CERT = 1;
internal const uint CERT_QUERY_CONTENT_CTL = 2;
internal const uint CERT_QUERY_CONTENT_CRL = 3;
internal const uint CERT_QUERY_OBJECT_FILE = 1;
internal const uint CERT_QUERY_FORMAT_BINARY = 1;
internal const uint CERT_QUERY_FORMAT_BASE64_ENCODED = 2;
internal const uint CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;
internal const uint CERT_QUERY_CONTENT_FLAG_CRL = (1 << (int)CERT_QUERY_CONTENT_CRL);
internal const uint CERT_QUERY_CONTENT_FLAG_CTL = (1 << (int)CERT_QUERY_CONTENT_CTL);
internal const uint CERT_QUERY_FORMAT_FLAG_BINARY = (1 << (int)CERT_QUERY_FORMAT_BINARY);
internal const uint CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = (1 << (int)CERT_QUERY_FORMAT_BASE64_ENCODED);
internal const uint CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = (1 << (int)CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED);
internal const uint CERT_QUERY_FORMAT_FLAG_ALL =
(CERT_QUERY_FORMAT_FLAG_BINARY |
CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED |
CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED);
}