How to get information from a CRL (.NET)

Hi all,

The following C# sample uses CryptoAPI to read the info of a CRL (Certificate Revocation List) stored in a file:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace GetCRLInfo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void getInfoButton_Click(object sender, EventArgs e)
        {
            // Variables
            //
            Boolean bResult = false;
            IntPtr pvContext = IntPtr.Zero;
            Win32.CRL_CONTEXT CRLContext;
            Win32.CRL_INFO CRLInfo;
            Int32 csz = 0;
            StringBuilder psz = null;
            IntPtr rgCRLEntry = IntPtr.Zero;
            Win32.CRL_ENTRY CRLEntry;
            String strSerialNumber = "";
            IntPtr pByte = IntPtr.Zero;
            Byte bByte = 0;
            IntPtr rgExtension = IntPtr.Zero;
            Win32.CERT_EXTENSION CRLExtension;
            Int32 cbFormat = 0;
            StringBuilder pbFormat = null;
            String strCRLReasonCode = "";
            
            // Clean screen
            //
            issuerTextBox.Text = "";
            revocationListBox.Items.Clear();

            try
            {
                // Get CRL context
                //
                bResult = Win32.CryptQueryObject(
                    Win32.CERT_QUERY_OBJECT_FILE,
                    fileTextBox.Text,
                    Win32.CERT_QUERY_CONTENT_FLAG_CRL,
                    Win32.CERT_QUERY_FORMAT_FLAG_BINARY,
                    0,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    ref pvContext
                );
                if (!bResult)
                {
                    throw new Exception("CryptQueryObject error #" + Marshal.GetLastWin32Error());
                }

                CRLContext = (Win32.CRL_CONTEXT)Marshal.PtrToStructure(pvContext, typeof(Win32.CRL_CONTEXT));

                // Get CRL info
                //
                CRLInfo = (Win32.CRL_INFO)Marshal.PtrToStructure(CRLContext.pCrlInfo, typeof(Win32.CRL_INFO));

                // Get CRL issuer
                //
                csz = Win32.CertNameToStr(
                    Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING,
                    ref CRLInfo.Issuer,
                    Win32.CERT_X500_NAME_STR,
                    null,
                    0
                );
                if (csz <= 0)
                {
                    throw new Exception("CertNameToStr error #" + Marshal.GetLastWin32Error());
                }

                psz = new StringBuilder(csz);

                csz = Win32.CertNameToStr(
                    Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING,
                    ref CRLInfo.Issuer,
                    Win32.CERT_X500_NAME_STR,
                    psz,
                    csz
                );
                if (csz <= 0)
                {
                    throw new Exception("CertNameToStr error #" + Marshal.GetLastWin32Error());
                }

                // Show CRL issuer
                //
                issuerTextBox.Text = psz.ToString();

                // Get revocation list
                //
                rgCRLEntry = CRLInfo.rgCRLEntry;
                for (int i = 0; i < CRLInfo.cCRLEntry; i++)
                {
                    // Get the serial number of one revoked certificate
                    //
                    strSerialNumber = "";

                    CRLEntry = (Win32.CRL_ENTRY)Marshal.PtrToStructure(rgCRLEntry, typeof(Win32.CRL_ENTRY));

                    pByte = CRLEntry.SerialNumber.pbData;
                    for (int j = 0; j < CRLEntry.SerialNumber.cbData; j++)
                    {
                        bByte = Marshal.ReadByte(pByte);
                        strSerialNumber = bByte.ToString("X").PadLeft(2, '0') + " " + strSerialNumber;
                        pByte = (IntPtr)((Int32)pByte + Marshal.SizeOf(typeof(Byte)));
                    }

                    // Get the CRL Reason Code of that revoked certificate
                    //
                    strCRLReasonCode = "";

                    rgExtension = Win32.CertFindExtension(
                        Win32.szOID_CRL_REASON_CODE,
                        CRLEntry.cExtension,
                        CRLEntry.rgExtension
                    );
                    if (rgExtension.Equals(IntPtr.Zero))
                    {
                        throw new Exception("CertFindExtension found no CRL Reason Code");
                    }
                    
                    CRLExtension = (Win32.CERT_EXTENSION)Marshal.PtrToStructure(rgExtension, typeof(Win32.CERT_EXTENSION));

                    // Format that CRL Reason Code so we can show it
                    //
                    cbFormat = 0;
                    pbFormat = null;
                    bResult = Win32.CryptFormatObject(
                        Win32.X509_ASN_ENCODING,
                        0,
                        0,
                        IntPtr.Zero,
                        Win32.szOID_CRL_REASON_CODE,
                        CRLExtension.Value.pbData,
                        CRLExtension.Value.cbData,
                        null,
                        ref cbFormat
                    );
                    if (!bResult)
                    {
                        throw new Exception("CryptFormatObject error #" + Marshal.GetLastWin32Error());
                    }

                    pbFormat = new StringBuilder(cbFormat);

                    bResult = Win32.CryptFormatObject(
                        Win32.X509_ASN_ENCODING,
                        0,
                        0,
                        IntPtr.Zero,
                        Win32.szOID_CRL_REASON_CODE,
                        CRLExtension.Value.pbData,
                        CRLExtension.Value.cbData,
                        pbFormat,
                        ref cbFormat
                    );
                    if (!bResult)
                    {
                        throw new Exception("CryptFormatObject error #" + Marshal.GetLastWin32Error());
                    }

                    strCRLReasonCode = pbFormat.ToString();

                    // Show Serial Number and CRL Reason Code
                    //
                    revocationListBox.Items.Add(strSerialNumber + "\t-->\t" + strCRLReasonCode);

                    // Continue with the next entry in the list
                    //
                    rgCRLEntry = (IntPtr)((Int32)rgCRLEntry + Marshal.SizeOf(typeof(Win32.CRL_ENTRY)));
                }
            }
            catch (Exception ex)
            {
                // Show errors
                //
                MessageBox.Show(ex.Message);
            }
            finally
            {
                // Do some clean up
                //
                if (!pvContext.Equals(IntPtr.Zero))
                {
                    Win32.CertFreeCRLContext(pvContext);
                }
            }
        }
    }
}

public class Win32
{
    #region APIs

    [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Boolean CryptQueryObject(
        Int32 dwObjectType,
        [MarshalAs(UnmanagedType.LPWStr)]String pvObject,
        Int32 dwExpectedContentTypeFlags,
        Int32 dwExpectedFormatTypeFlags,
        Int32 dwFlags,
        IntPtr pdwMsgAndCertEncodingType,
        IntPtr pdwContentType,
        IntPtr pdwFormatType,
        IntPtr phCertStore,
        IntPtr phMsg,
        ref IntPtr ppvContext
        );

    [DllImport("CRYPT32.DLL", EntryPoint = "CertFreeCRLContext", SetLastError = true)]
    public static extern Boolean CertFreeCRLContext(
        IntPtr pCrlContext
    );

    [DllImport("CRYPT32.DLL", EntryPoint = "CertNameToStr", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Int32 CertNameToStr(
        Int32 dwCertEncodingType,
        ref CRYPTOAPI_BLOB pName,
        Int32 dwStrType,
        StringBuilder psz,
        Int32 csz
    );

    [DllImport("CRYPT32.DLL", EntryPoint = "CertFindExtension", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CertFindExtension(
        [MarshalAs(UnmanagedType.LPStr)]String pszObjId,
        Int32 cExtensions,
        IntPtr rgExtensions
    );

    [DllImport("CRYPT32.DLL", EntryPoint = "CryptFormatObject", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Boolean CryptFormatObject(
        Int32 dwCertEncodingType,
        Int32 dwFormatType,
        Int32 dwFormatStrType,
        IntPtr pFormatStruct,
        [MarshalAs(UnmanagedType.LPStr)]String lpszStructType,
        IntPtr pbEncoded,
        Int32 cbEncoded,
        StringBuilder pbFormat,
        ref Int32 pcbFormat
    );

    #endregion APIs

    #region Structs

    [StructLayout(LayoutKind.Sequential)]
    public struct CRL_CONTEXT
    {
        public Int32 dwCertEncodingType;
        public IntPtr pbCrlEncoded;
        public Int32 cbCrlEncoded;
        public IntPtr pCrlInfo;
        public IntPtr hCertStore;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CRL_INFO
    {
        public Int32 dwVersion;
        public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
        public CRYPTOAPI_BLOB Issuer;
        public FILETIME ThisUpdate;
        public FILETIME NextUpdate;
        public Int32 cCRLEntry;
        public IntPtr rgCRLEntry;
        public Int32 cExtension;
        public IntPtr rgExtension;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CRYPT_ALGORITHM_IDENTIFIER
    {
        [MarshalAs(UnmanagedType.LPStr)]public String pszObjId;
        public CRYPTOAPI_BLOB Parameters;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CRYPTOAPI_BLOB
    {
        public Int32 cbData;
        public IntPtr pbData;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FILETIME
    {
        public Int32 dwLowDateTime;
        public Int32 dwHighDateTime;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CRL_ENTRY
    {
        public CRYPTOAPI_BLOB SerialNumber;
        public FILETIME RevocationDate;
        public Int32 cExtension;
        public IntPtr rgExtension;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CERT_EXTENSION
    {
        [MarshalAs(UnmanagedType.LPStr)]public String pszObjId;
        public Boolean fCritical;
        public CRYPTOAPI_BLOB Value;
    }

    #endregion Structs

    #region Consts

    public const Int32 CERT_QUERY_OBJECT_FILE = 0x00000001;
    public const Int32 CERT_QUERY_CONTENT_CRL = 3;
    public const Int32 CERT_QUERY_CONTENT_FLAG_CRL = 1 << CERT_QUERY_CONTENT_CRL;
    public const Int32 CERT_QUERY_FORMAT_BINARY = 1;
    public const Int32 CERT_QUERY_FORMAT_BASE64_ENCODED = 2;
    public const Int32 CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;
    public const Int32 CERT_QUERY_FORMAT_FLAG_BINARY = 1 << CERT_QUERY_FORMAT_BINARY;
    public const Int32 CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = 1 << CERT_QUERY_FORMAT_BASE64_ENCODED;
    public const Int32 CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = 1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED;
    public const Int32 CERT_QUERY_FORMAT_FLAG_ALL = CERT_QUERY_FORMAT_FLAG_BINARY | CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED | CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED;

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

    public const Int32 X509_NAME = 7;

    public const Int32 CERT_SIMPLE_NAME_STR = 1;
    public const Int32 CERT_OID_NAME_STR = 2;
    public const Int32 CERT_X500_NAME_STR = 3;

    public const String szOID_CRL_REASON_CODE = "2.5.29.21";

    #endregion
}

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)