8

I need to download and install about 50 CRLs once a week and install them on several Windows servers. Downloading is the easy part, is there a way I could script the CRL import process?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Goyuix
  • 23,614
  • 14
  • 84
  • 128
  • When you say downloading is the easy part? Can you share your technique that you used to accomplish this. Thanks – Raj Jun 17 '11 at 10:43
  • @Raj pretty much every certificate authority or signer will publish a CRL location as part of the certificate metadata. Open up the certificate in question and look for a field called "CRL Distribution Points". Really this is a general question you should ask not just leave as a comment. – Goyuix Jun 18 '11 at 16:40
  • Yes, i am aware of the Distribution points as a field in the certificate. Currently i use Remote Object Retrieval Functions,[CryptoAPI] to retrieve from the CDP's. I also understand that if you just copy the URL and past them in the browser, CRL will get downloaded as well. The reason why i asked you as a comment is that you mentioned that CRL retrieval is the easy part, so i assumed that you have got some utility or something which will do this for you. But obviously my assumption was wrong. Thanks for responding. – Raj Jun 20 '11 at 09:49

5 Answers5

13

Here is my final source (slightly scrubbed for the public) - but should work. I won't change the accepted answer, but I do hope this helps (as does upvoting the question and answers!).

Note: This will import both a CRL or a regular certificate into the LOCAL MACHINE Trusted Root store. Changing the below CERT_SYSTEM_STORE_LOCAL_MACHINE to CERT_SYSTEM_STORE_CURRENT_USER in the call CertOpenStore will change it to work for the Current User store.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication2
{
  class Program
  {
    public struct CRYPTUI_WIZ_IMPORT_SRC_INFO
    {
      public Int32 dwSize;
      public Int32 dwSubjectChoice;
      [MarshalAs(UnmanagedType.LPWStr)]public String pwszFileName;
      public Int32 dwFlags;
      [MarshalAs(UnmanagedType.LPWStr)]public String pwszPassword;
    }

    [DllImport("CryptUI.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Boolean CryptUIWizImport(
      Int32 dwFlags,
      IntPtr hwndParent,
      IntPtr pwszWizardTitle,
      ref CRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc,
      IntPtr hDestCertStore
    );

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

    public const Int32 CRYPTUI_WIZ_IMPORT_SUBJECT_FILE = 1;
    public const Int32 CRYPT_EXPORTABLE = 0x00000001;
    public const Int32 CRYPT_USER_PROTECTED = 0x00000002;
    public const Int32 CRYPTUI_WIZ_NO_UI = 0x0001;

    private static int CERT_STORE_PROV_SYSTEM = 10;
    private static int CERT_SYSTEM_STORE_CURRENT_USER = (1 << 16);
    private static int CERT_SYSTEM_STORE_LOCAL_MACHINE = (2 << 16);

    static void Main(string[] args)
    {
      if (args.Length != 1)
      {
        Console.WriteLine("Usage: certimp.exe list.crl");
        Environment.ExitCode = 1;
      }
      else
      {
        IntPtr hLocalCertStore = CertOpenStore(
          CERT_STORE_PROV_SYSTEM,
          0,
          IntPtr.Zero,
          CERT_SYSTEM_STORE_LOCAL_MACHINE,
          "ROOT"
        );

        CRYPTUI_WIZ_IMPORT_SRC_INFO importSrc = new CRYPTUI_WIZ_IMPORT_SRC_INFO();
        importSrc.dwSize = Marshal.SizeOf(importSrc);
        importSrc.dwSubjectChoice = CRYPTUI_WIZ_IMPORT_SUBJECT_FILE;
        importSrc.pwszFileName = args[0];
        importSrc.pwszPassword = null;
        importSrc.dwFlags = CRYPT_EXPORTABLE | CRYPT_USER_PROTECTED;

        if (!CryptUIWizImport(
            CRYPTUI_WIZ_NO_UI,
            IntPtr.Zero,
            IntPtr.Zero,
            ref importSrc,
            hLocalCertStore
          ))
        {
          Console.WriteLine("CryptUIWizImport error " + Marshal.GetLastWin32Error());
          Environment.ExitCode = -1;
        }
      }
    }
  }
}
garyh
  • 2,782
  • 1
  • 26
  • 28
Goyuix
  • 23,614
  • 14
  • 84
  • 128
  • 1
    Note: You can also create an X509Store, Open() it and pass its StoreHandle property as the last argument to CryptUIWizImport. – voetsjoeba Dec 29 '13 at 21:05
  • Note: Despite what the documentation says, none of the CRYPTUI_WIZ_NO_UI flags prevent a popup dialog that asks "Do you want to replace the current CRL?" when the target store already contains a CRL with the same or newer validity time. (Tested on Win2008 R2) – voetsjoeba Dec 29 '13 at 21:10
  • 1
    Note: For a more direct way of adding a CRL to a Windows certificate store than (potentially) going through a UI, you can P/Invoke the CertAddCRLContextToStore Win32 function from crypt32.dll. – voetsjoeba Dec 29 '13 at 23:00
4

I don't know a way to do it via script. Can you write C code? If I understand what you want to do, you will use the CryptUiWizImport function, and the CRYPTUI_WIZ_IMPORT_SRC_INFO structure.

Here's a sample of code that installs a Cert; the corresponding CRL import is similar.

Addendum:
This post points out that Win32 APIs (such as CryptUiWizImport) are not directly accessible from PowerShell, and then describes a possible workaround: from within the PowerShell script, dynamically generate and compile C# code that does the P/Invoke stuff, and then run the resulting assembly. This would allow you to do the CryptUiWizImport strictly from a powershell script, although it would be a pretty exotic one.

Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • 1
    I wrapped the CryptUIWizImport function as a C# console application and it works like a charm for installing both certs and CRLs. Thank you very much! – Goyuix May 27 '09 at 13:35
  • 1
    You interested in posting the source, to help the next person? – Cheeso May 27 '09 at 22:07
  • I would be really interested in the source... for I have exactly the same problem. – Mephisztoe Jul 14 '09 at 10:29
3

Hm. Is there any reason not to use the certutil.exe utility? I can import a Certificate Revocation List into the appropriate store by running the following command:

certutil -addstore CA <FileName>.crl
Mephisztoe
  • 3,276
  • 7
  • 34
  • 48
  • certutil is not part of a standard installation. I believe it only is installed when certificate services have been installed (though I could be wrong). – Goyuix Jul 14 '09 at 17:01
1

In Powershell there is a Cert: provider which represents the certificate store. Manipulating it is done via the standard cmdlets so you might be able to integrate a revocation list there somewhere. I just don't know enough about how Windows handles certificates to be of any further help here.

Joey
  • 344,408
  • 85
  • 689
  • 683
0

We have to use only Win32 Apis to do this. There is no firstclass C# system APIs to do this. Refer https://stackoverflow.com/a/67814697/3671594

saravanan
  • 398
  • 4
  • 13