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)
Comments
- Anonymous
April 02, 2009
Hi !Thanks for sharing this ! It is very usefull !I am not at ease with C/C++ things... Can you tell me how to get the CRL infos from a C# "byte[]" instead of a file ?Thanks again !Damien - Anonymous
April 03, 2009
Sorry, I have no sample for that. If you need help I suggest you open a case with us, Microsoft Technical Support.Thx,Alex - Anonymous
July 04, 2009
I also need to use .NET code to check CRL check but I need to use VeriSign URL to check the Revocation (CRL) of Certificate before I send the certificate along with the WebService call from windows client application. I find X509Chain class in .NET which has RevocationFlag, RevocationMode but not sure how to implement using URL that is provided by VeriSign.How to implement the same in Online Certificate Status Protocol (OCSP), certificate revocation lists (CRLs) for revocation information about X.509 certificates. CRLs are publicly available from distribution points like HTTP or LDAP servers. A certificate usually contains a CRLDistributionPoints extension with a link to the location from where the corresponding CRL can be obtained.Please let me known is there any way to fetch CRL distribution point from X509Certificate using .NET Framework 2.0, 3.5, CAPICOM or Windows API.I appreciate your guidance on this.Thanks - Anonymous
February 07, 2010
hello AlejandroAbove all i tell you thanks for you time and dedication to this messageI appreciate your guidance on this.i have one problem with a application developed in c# i need read mail of exchange server the mail is encrypted with a Digital ID of VerigSign, in this moment i only need to decrypt the message.Thanks for allnote: sorry my english not is goodi hope understand mesee you - Anonymous
April 23, 2010
I found this to be a useful example. It makes me wonder; however, why there is not a class in .NET for this (as there is for an X509Certificate). Also, I am having difficulty converting your FILETIME structure to a useful DateTime. Any info is appreciated.Thanks in Advance .. John - Anonymous
April 25, 2010
Instead of the FILETIME struct, which is formed by 2-32bit numbers, you may use type Int64 (long), which is 1-64 bit number. DateTime class has methods to work with filetime:"long lTime;DateTime oTime;lTime = DateTime.Now.ToFileTime();oTime = DateTime.FromFileTime(lTime);"Hope this helps. - Anonymous
January 05, 2011
I am getting error CryptQueryObject error #-2146885623 while using the code.I am passing fileTextBox.Text value as "MyRevoactionList.crl" - Anonymous
January 06, 2011
Sorry Vinay, don't know why you are getting that error. I would need to debug it. If you need help, please open a support case with us.Regards,Alex - Anonymous
February 10, 2011
Vinay Singh - check you CRL file. file format must be CERT_QUERY_FORMAT_FLAG_BINARY.If you CRL file in base64 format delete header ("-----BEGIN X509 CRL-----") and ("-----END X509 CRL-----") is theses present and convert to binary/ - Anonymous
February 22, 2011
For a custom implementation it is not recommended to use CryptQueryObject, instead you should use CertCreateCRLContext function. In addition CertCreateCRLContext accepts a byte array (instead of a file). - Anonymous
February 24, 2011
First - Great article!Read CRL's on the fly -public FILETIME ThisUpdate;public FILETIME NextUpdate;public Int32 dwLowDateTime;public Int32 dwHighDateTime;Returned as Int32 - How do convert this to DateTime?Regards Carl Gustav - Anonymous
April 04, 2011
Thanks for the code I use it in my work with grat succes. I have writen this utility method to convert from FILETAME Win32 to .Net DateTime:<code> public static DateTime FileTimeToDateTime(Win32.FILETIME fileTime) { DateTime dateTime; IntPtr int64Ptr = Marshal.AllocHGlobal(sizeof(Int64)); try { Marshal.StructureToPtr(fileTime, int64Ptr, true); dateTime = DateTime.FromFileTime(Marshal.ReadInt64(int64Ptr)); } finally { Marshal.FreeHGlobal(int64Ptr); } return dateTime; }</code> - Anonymous
September 06, 2012
Extracting the Certificate Serial Number on Windows Server 2012 is causing a overflow exception when doing:pByte = ( IntPtr ) ( ( Int32 ) pByte + Marshal . SizeOf ( typeof ( Byte ) ) ) ; - Anonymous
September 06, 2012
Extracting the Certificate Serial Number on Windows Server 2012 is causing a overflow exception when doing:pByte = ( IntPtr ) ( ( Int32 ) pByte + Marshal . SizeOf ( typeof ( Byte ) ) ) ; - Anonymous
January 23, 2013
The comment has been removed - Anonymous
February 16, 2015
Can we read serial number for all certificate that stored in CRL?. Above one gives only info of CRL