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:
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 ); } }
I hope it helps.
Regards,
Alex (Alejandro Campos Magencio)
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.