Programmatically installing certificate revocation list C#
Asked Answered
L

3

1

I am using C#/WCF. I have a web service which shall be invoked by the client. This is the service definition:

<service behaviorConfiguration="WCFInterface.CommonBehavior" name="WCFInterface.Content">
  <endpoint address="" binding="ws2007HttpBinding" bindingConfiguration="wsHttpUserName"
 contract="ABB.fTunes.WCFInterface.IContent">
    <identity>
      <dns value="fTunesTestServer" />
    </identity>
  </endpoint>
  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>

And this is the binding:

<ws2007HttpBinding>
  <binding name="wsHttpUserName">
    <security mode="Message">
      <message clientCredentialType="UserName"/>
    </security>
  </binding>
</ws2007HttpBinding>

If I understand this correctly, the messages sent from server to client are encrypted with a certificate. Currently I am still working with developer certificates. I created a root certificate, a certificate revokation list and a key on the server.

I am installing the client with Windows Installer and I have a custom install action to install the certificates.

The following code shows how the certificates are added to the store

Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyRoot.cer");
byte[] buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
manifestResourceStream.Close();

var cert = new X509Certificate2(buffer);
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();

/*
// The CRL is also needed, no idea why
manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyRoot.crl");
buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
manifestResourceStream.Close();
cert = new X509Certificate2(buffer);
store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
*/

// This is the key 
manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClientCertificates.MyTestServer.cer");
buffer = new byte[((int)(manifestResourceStream.Length - 1L)) + 1];
manifestResourceStream.Read(buffer, 0, (int)manifestResourceStream.Length);
manifestResourceStream.Close();

cert = new X509Certificate2(buffer);
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();

I have now two behaviors: Installing the certificates work, but when I call the web service I get a SecurityNegotiationException. When I add the Certificate Revocation List manually, the communication with the server works. When I try to do it programmatically (see code above) it does not work. I get a "Could not find requested object" exception.

I tried to use different stores but with no success.

I have two questions: a) Why do I need the CRL on the client? b) If I need it, how can I install it programmatically? Where is my mistake above?

Thanks for your help, Kay

Log answered 25/9, 2009 at 12:19 Comment(0)
P
4

Generally, the CRL has to be available online and downloadable from the revocation URL specified in the server cert. I don't know if there's an out-of-band mechanism for getting it, but even if there was, it'd kinda defeat the purpose (allowing clients to discover that a server cert has been compromised/revoked). That said, a CRL is really overkill for self-signed certs unless you're using the cert for real mutual authentication and you're worried about the key being compromised (in which case, buy a commercial cert and let them deal with it).

If you can't get a cert generated without a revocation URL, I'd recommend disabling the client check of the CRL altogether unless you really need it. You can do this by adding the following to the webservice client's app.config:

  <system.net>
    <settings>
      <servicePointManager checkCertificateRevocationList="false"/>
    </settings>
  </system.net>

If you're using WCF, you may need to wire it up with a clientCredentials endpointBehavior, under serviceCertificate->revocationMode:NoCheck instead.

Perceive answered 26/9, 2009 at 8:16 Comment(0)
O
2

Ways of automating the install of a Certificate Revocation List, including via C# code, can be found here: Programatically install Certificate Revocation List (CRL)

Overleap answered 21/10, 2013 at 11:3 Comment(0)
P
0

We have to use only Win32 Apis to do this. There is no firstclass C# system APIs to do this.

public class CRLHandler
{
    private const int CERT_STORE_PROV_SYSTEM = 10;
    private const int CERT_SYSTEM_STORE_LOCAL_MACHINE = (2 << 16);

    public const int X509_ASN_ENCODING = 0x00000001;
    public const int PKCS_7_ASN_ENCODING = 0x00010000;

    public const int CERT_STORE_ADD_REPLACE_EXISTING = 3;

    [DllImport("CRYPT32.DLL", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CertOpenStore(
      int storeProvider,
      int encodingType,
      IntPtr hcryptProv,
      int flags,
      string pvPara);

    [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CertCreateCRLContext(
        int dwCertEncodingType,
        byte[] pbCrlEncoded,
        int cbCrlEncoded);

    [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool CertAddCRLContextToStore(
      IntPtr hCertStore,
      IntPtr pCertContext,
      uint dwAddDisposition,
      IntPtr ppStoreContext);

    [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool CertFreeCRLContext(
        IntPtr pCrlContext);

    public void AddOrUpdateCRLToStore(string crlString)
    {
        IntPtr crlContext = IntPtr.Zero;
        try
        {
            byte[] rawData = Convert.FromBase64String(crlString);

            IntPtr hLocalCertStore = CertOpenStore(
                  CERT_STORE_PROV_SYSTEM,
                  0,
                  IntPtr.Zero,
                  CERT_SYSTEM_STORE_LOCAL_MACHINE,
                  "CA");

            crlContext = CertCreateCRLContext(
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                rawData,
                rawData.Length);

            if (crlContext == IntPtr.Zero)
            {
                string error = "AddOrUpdateCRLToStore - CertCreateCRLContext error #" + Marshal.GetLastWin32Error();
                throw new Exception(error);
            }

            bool crlAddResult = CertAddCRLContextToStore(
                hLocalCertStore, crlContext, CERT_STORE_ADD_REPLACE_EXISTING, IntPtr.Zero);

            if (!crlAddResult)

            {
                string error = "AddOrUpdateCRLToStore - CertAddCRLContextToStore #" + Marshal.GetLastWin32Error();
                throw new Exception(error);
            }
        }
        finally
        {
            if(crlContext != IntPtr.Zero)
            {
                CertFreeCRLContext(crlContext);
            }
        }
    }
}

The AddOrUpdateCRLToStore method takes a base64 encoded crl string (without the header and tailer) and then imports the crl to the CA store of local machine.

To modify the values like local machine, cert store in this APIs refer this

Prejudicial answered 3/6, 2021 at 2:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.