Поделиться через


How to get info from client certificates issued by a CA (C#)

Hi all,

The following C# sample shows how to use Certadm.dll and CryptoAPI to get the name of the template and the enhanced usages of client certificates in a CA

<SAMPLE file="Form1.cs">

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

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

        private void button1_Click(object sender, EventArgs e)
        {
            // Variables
            string strServerName = "MyServer";
            DirectoryEntry rootEntry = null;
            DirectoryEntry templatesEntry = null;

            try
            {
                // Get AD entry that we will use to translate a certificate template OID to its correspondent name
                rootEntry = new DirectoryEntry("LDAP://" + strServerName + "/rootDSE");
                templatesEntry = new DirectoryEntry("LDAP://" + strServerName + "/cn=certificate templates,cn=public key services,cn=services,cn=configuration," + (string)rootEntry.Properties["defaultNamingContext"][0]);

                // Get Certificate Services Database info
                ViewCertificateServicesDatabase(strServerName, strServerName, templatesEntry);
            }
            catch (Exception ex)
            {
                // Errors?
                MessageBox.Show(ex.Message);
            }
            finally
            {
                // Clean up
                if (rootEntry != null)
                {
                    rootEntry.Dispose();
                }
                if (templatesEntry != null)
                {
                    templatesEntry.Dispose();
                }
            }
        }

        private void ViewCertificateServicesDatabase(string strServer, string strCAName, DirectoryEntry templatesEntry)
        {
            // Variables
            CERTADMINLib.CCertView certView = null;
            CERTADMINLib.IEnumCERTVIEWROW certViewRow = null;
            CERTADMINLib.IEnumCERTVIEWCOLUMN certViewColumn = null;
            CERTADMINLib.IEnumCERTVIEWEXTENSION certViewExt = null;
            int iColumnCount = 0;
            string strBase64Value = "";
            string strValue = "";
            string strOID = "";
            int iStartIndex = 0;
            string strDisplayName = "";
            object objValue = null;
            string strOutput = "";

            // Connecting to the Certificate Authority
            certView = new CERTADMINLib.CCertViewClass();
            certView.OpenConnection(strServer + "\\" + strCAName);

            // Get a column count and place columns into the view
            iColumnCount = certView.GetColumnCount(0);
            certView.SetResultColumnCount(iColumnCount);

            // Place each column in the view.
            for (int x = 0; x < iColumnCount; x++)
            {
                certView.SetResultColumn(x);
            }

            // Open the View and reset the row position
            certViewRow = certView.OpenView();
            certViewRow.Reset();

            // Enumerate Row and Column Information

            // Rows         
            for (int x = 0; certViewRow.Next() != -1; x++)
            {
                // Extensions
                strOutput = "ROW #" + x.ToString() + " EXTENSIONS\n\n";
                certViewExt = certViewRow.EnumCertViewExtension(0);
                certViewExt.Reset();

                while (certViewExt.Next() != -1)
                {
                    switch (certViewExt.GetName())
                    {
                        // Certificate Template
                        case "1.3.6.1.4.1.311.21.7":

                            // Certificate Template OID, Mayor Version Number and Minor Version Number
                            strBase64Value = (string)certViewExt.GetValue(Win32.PROPTYPE_BINARY, Win32.CV_OUT_BASE64);
                            strValue = FormatObject("1.3.6.1.4.1.311.21.7", Convert.FromBase64String(strBase64Value));
                            strOutput += "Certificate Template OID = \"" + strValue + "\"\n\n";

                            strDisplayName = "";
                            if (strValue.StartsWith("Template="))
                            {
                                // Certificate Template OID
                                iStartIndex = strValue.IndexOf("=") + 1;
                                strOID = strValue.Substring(iStartIndex, strValue.IndexOf(",") - iStartIndex);

                                // Certificate Template Display Name
                                strDisplayName = TranslateTemplateOID(strOID, templatesEntry);
                            }
                            strOutput += "Certificate Template Display Name = \"" + strDisplayName + "\"\n\n";
                            break;

                        // Enhanced Key Usage
                        case "2.5.29.37":
                            strBase64Value = (string)certViewExt.GetValue(Win32.PROPTYPE_BINARY, Win32.CV_OUT_BASE64);
                            strValue = FormatObject("2.5.29.37", Convert.FromBase64String(strBase64Value));
                            strOutput += "Enhanced Key Usage = \"" + strValue + "\"\n\n";
                            break;

                        default:
                            break;
                    }
                }

                // Columns
                strOutput += "ROW #" + x.ToString() + " COLUMNS\n\n";
                certViewColumn = certViewRow.EnumCertViewColumn();
                while (certViewColumn.Next() != -1)
                {
                    switch (certViewColumn.GetDisplayName())
                    {
                        // Certificate Template
                        case "Certificate Template":
                            objValue = certViewColumn.GetValue(Win32.PROPTYPE_STRING);
                            if (objValue != null)
                            {
                                strOutput += "Certificate Template Name = \"" + objValue.ToString() + "\"\n\n";
                            }
                            else
                            {
                                strOutput += "Certificate Template Name = \"\"\n\n";
                            }
                            break;

                        // "Certificate Expiration Date"
                        case "Certificate Expiration Date":
                            objValue = certViewColumn.GetValue(Win32.PROPTYPE_DATE);
                            if (objValue != null)
                            {
                                strOutput += "Certificate Expiration Date = \"" + objValue.ToString() + "\"\n\n";
                            }
                            else
                            {
                                strOutput += "Certificate Expiration Date = \"\"\n\n";
                            }
                            break;

                        default:
                            break;
                    }
                }

                // Show row info
                MessageBox.Show(strOutput);
            }
        }

        string FormatObject(string strOID, byte[] pbEncoded)
        {
            // Variables
            IntPtr pbFormat = IntPtr.Zero;
            int cbFormat = 0;
            string strValue = "";
            bool bWorked = false;

            try
            {
                // Get size for decoded data
                bWorked = Win32.CryptFormatObject(
                    Win32.X509_ASN_ENCODING,
                    0,
                    Win32.CRYPT_FORMAT_STR_NO_HEX,
                    IntPtr.Zero,
                    strOID,
                    pbEncoded,
                    pbEncoded.Length,
                    IntPtr.Zero,
                    ref cbFormat
                );
                if (!bWorked) { throw new Win32Exception(Marshal.GetLastWin32Error()); }

                // Create buffer for decoded data
                pbFormat = Marshal.AllocHGlobal(cbFormat);

                // Get decoded data
                bWorked = Win32.CryptFormatObject(
                    Win32.X509_ASN_ENCODING,
                    0,
                    Win32.CRYPT_FORMAT_STR_NO_HEX,
                    IntPtr.Zero,
                    strOID,
                    pbEncoded,
                    pbEncoded.Length,
                    pbFormat,
                    ref cbFormat
                );
                if (!bWorked) { throw new Win32Exception(Marshal.GetLastWin32Error()); }

                strValue = Marshal.PtrToStringUni(pbFormat);
            }
            catch (Exception ex)
            {
                // Error?
                strValue = "FormatObject error: " + ex.Message;
            }
            finally
            {
                // Clean up
                if (!pbFormat.Equals(IntPtr.Zero))
                {
                    Marshal.FreeHGlobal(pbFormat);
                }
            }

            return strValue;
        }

        string TranslateTemplateOID(string strOID, DirectoryEntry templatesEntry)
        {
            // Variables
            DirectorySearcher searcher = null;
            SearchResult result = null;
            string strDisplayName = "";

            try
            {
                // Look for the Display Name of a template OID in AD
                searcher = new DirectorySearcher(templatesEntry);
                searcher.Filter = "(&(msPKI-Cert-Template-OID=" + strOID + ")) ";
                result = searcher.FindOne();
                strDisplayName = (string)result.Properties["displayName"][0];
            }
            catch (Exception ex)
            {
                // Error?
                strDisplayName = "TranslateTemplateOID error: " + ex.Message;
            }
            finally
            {
                // Clean up
                if (searcher != null)
                {
                    searcher.Dispose();
                }
            }

            return strDisplayName;
        }
    }
}

</SAMPLE>

<SAMPLE file="Win32.cs">

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

namespace CertAdminTest
{
    class Win32
    {
        public const int X509_ASN_ENCODING = 0x00000001;

        public const int CRYPT_FORMAT_STR_NO_HEX = 0x0010;

        public const int CV_OUT_BASE64 = 0x1;

        public const int PROPTYPE_DATE = 0x2;
        public const int PROPTYPE_BINARY = 0x3;
        public const int PROPTYPE_STRING = 0x4;

        [DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
        public static extern Boolean CryptFormatObject(
            int dwCertEncodingType,
            int dwFormatType,
            int dwFormatStrType,
            IntPtr pFormatStruct,
            String lpszStructType,
            Byte[] pbEncoded,
            int cbEncoded,
            IntPtr pbFormat,
            ref int pcbFormat
        );
    }
}

</SAMPLE> 

 

I hope it helps. 

Regards,

 

Alex (Alejandro Campos Magencio)

Comments

  • Anonymous
    April 18, 2012
    The ViewCertificateServicesDatabase function does not properly cleanup after itself.It should release certView and certViewRow and it should release and certViewExt, certViewColumn need to be released each time they are assigned.