How to Verify the Validity of IIS Certificates on Remote Servers Using ABO – Part 2

Nawal Kishore Gupta here. I am a developer on the Information Security Tools team focused on building host security assessment tools.

This is the second part of a two part blog on how to verify the validity of IIS certificates on remote servers. The first part is posted here.

Reading and Displaying the Certificate.

ReadAndDisplayCertificate(strServerName,strStoreName,dwBinHashSize,&binSSLHash[0]);

This function contains the logic to parse the certificate.

The steps for this function are.

1. Add server name with store name

HRESULT hr=S_OK;

      HCERTSTORE  hSystemStore=NULL;          // The system store handle.

      PCCERT_CONTEXT  pDesiredCert=NULL ;   // Set to NULL for the first time

      CString strStoreFullPath;

if (lpServerName && lpStoreName)

      {

            strStoreFullPath.Format(L"\\\\%s\\%s",lpServerName,lpStoreName);

      }

else if (lpStoreName)

      {

            strStoreFullPath=lpStoreName;

      }

2. Open Certificate Store

hSystemStore = CertOpenStore(

                         CERT_STORE_PROV_SYSTEM,

                         0,                      // Encoding type not needed

// with this PROV.

                         NULL,                   // Accept the default HCRYPTPROV.

                         CERT_SYSTEM_STORE_LOCAL_MACHINE,

// Set the system store location in

// the registry.

                         strStoreFullPath);

3. Search Certificate in Certificate Store using CertFindCertificateInStore

CRYPT_UINT_BLOB pBlob;

      pBlob.pbData=binSSLHash;

      pBlob.cbData=nBinHashSize;

      pDesiredCert=CertFindCertificateInStore(

                          hSystemStore,

                          (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING),           // Use X509_ASN_ENCODING.

                          0,                          // No dwFlags needed.

                          CERT_FIND_HASH,      // Find a certificate with a     // subject that matches the string   // in the next parameter.

                          &pBlob ,           // The Unicode string to be found        // in a certificate's subject.

                          NULL) ;

if (!pDesiredCert)

return FALSE;

4. Read the Values from the Certificate using CertGetNameString.

You can use the following definitions.

#define CERT_NAME_ISSUEDTO_FLAG  0

int cbSize = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUER_FLAG,

            NULL,  

            NULL,  

            0);

       CString strName;

       CString strIssuerName;

       CString strIssuedToName;

       TCHAR * pszName =NULL;

if (cbSize>1)

       {

             pszName = strName.GetBuffer( cbSize+1);

             BOOL bConv = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUER_FLAG,

            NULL,  

            pszName,  

            cbSize);

                  strName.ReleaseBuffer();

if (bConv)

             {

                  strIssuerName=strName;             

                  printf("Certificate Issued By: %S\n",(LPCTSTR) strIssuerName);

             }

       }

       cbSize = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUEDTO_FLAG,

            NULL,  

            NULL,  

            0);

      pszName =NULL;

if (cbSize>1)

       {

             pszName = strName.GetBuffer( cbSize+1);

             BOOL bConv = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUEDTO_FLAG,

            NULL,  

            pszName,  

            cbSize);

                  strName.ReleaseBuffer();

if (bConv)

             {

                  strIssuedToName=strName;                 

                  printf("Certificate Issued To: %S\n",(LPCTSTR) strIssuedToName);

//printf

             }

       }

      CERT_INFO  * pCertInfo = pDesiredCert->pCertInfo;

      SYSTEMTIME st;

      FILETIME lft;  

     FileTimeToLocalFileTime((const FILETIME *)&pCertInfo->NotBefore, &lft);

     FileTimeToSystemTime(( const FILETIME *) &pCertInfo->NotBefore, &st);

       printf(("Certificate Not Valid Before (MM/DD/YY HH:MM) : %02d/%02d/%04d %02d:%02d\n"),

                                            st.wMonth,

                                            st.wDay,

                                            st.wYear,

                                            st.wHour,

                                            st.wMinute);

        FileTimeToSystemTime(( const FILETIME *) &pCertInfo->NotAfter, &st);

            printf(("Certificate Not Valid After (MM/DD/YY HH:MM) : %02d/%02d/%04d %02d:%02d\n"),

                                            st.wMonth,

                                            st.wDay,

                                            st.wYear,

                                            st.wHour,

                                            st.wMinute);

int nValid= CertVerifyTimeValidity(

      NULL,               // Use current time.

      pCertInfo);

      CString strMessage;

switch(nValid)   // Pointer to CERT_INFO.

      {

case -1 :

            {

                  strMessage = (L"Certificate is not valid yet. \n");

break;

            }

case 1:

            {

                  strMessage = (L"Certificate is expired. \n");

break;

            }

case 0:

            {

                  strMessage = (L"Certificate's time is valid. \n");               

break;

            }

      }

      printf("%S\n", (LPCTSTR) strMessage);

return TRUE;

Hope this article will help you to manage your certificates automatically and alert you whenever any certificate is expired.

Complete Code Listing

// AboDemo.cpp : Defines the entry point for the console application.

//

//

#include "StdAfx.h"

// StdAfx.h should contains only

//#include "targetver.h"

//#include <stdio.h>

//#include <tchar.h>

//Remove other files from StdAfx.h

#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit

#include <initguid.h>

#include <wchar.h>

#include <objbase.h>

#include <ks.h>

#include <stdio.h>

#include <iadmw.h> // COM Interface header file.

#include <iiscnfg.h> // MD_ & IIS_MD_ #defines header file.

#include <objidl.h>

#include <atlBase.h> // ATL support header file

#include <atlstr.h>

//For Certificate

#include <Wincrypt.h>

#pragma comment(lib, "Crypt32.lib")

//This definations are missing in iadmw.

// Got from some research on net and MSDN

#define MD_SSL_CERT_HASH  5506

#define MD_SSL_STORE_NAME  5511

#define MD_READ_TIMEOUT  40

#define MAX_CERT_SIZE   1024

#define CERT_NAME_ISSUEDTO_FLAG  0

#define BUFFER_LEN 1024

#define MAX_BUFFER_SIZE 65536

#define HTAB_SIZE 517

//Must Change

#define SERVERNAME L"remoteservername"

//You must be an admin in remote machine or use admin token to run this application

BOOL ReadAndDisplayCertificate(LPCTSTR lpszServerName,LPCTSTR lpszStoreName,int nBinHashSize, BYTE *binSSLHash);

int _tmain(int argc, _TCHAR* argv[])

{

//Some error handling omitted for sake of brevity

      HRESULT hresError = CoInitializeEx(NULL, COINIT_MULTITHREADED);

//IMSAdminBase DCOM Object Pointer

      CComPtr <IMSAdminBase> pIAdminBase;

      IClassFactory * pcsfFactory = NULL;

      COSERVERINFO csiMachineName;

      memset(&csiMachineName,0, sizeof(COSERVERINFO));     

if (argc>=2)

      {

            csiMachineName.pwszName = argv[1];

      }

else

      {

            csiMachineName.pwszName = SERVERNAME;

      }

      CString strServerName(csiMachineName.pwszName);

//pcsiParam = &csiMachineName;

      hresError = CoGetClassObject(GETAdminBaseCLSID(TRUE), CLSCTX_SERVER, &csiMachineName,IID_IClassFactory, (void**) &pcsfFactory);

if (FAILED(hresError)) 

      {

            printf ("ERROR: CoGetClassObject Failed! Error: %d (%#x)\n", hresError, hresError);

return hresError;

      }

else

      {

            hresError = pcsfFactory->CreateInstance(NULL, IID_IMSAdminBase, (void **) &pIAdminBase);

if (FAILED(hresError)) 

            {

                  printf ("ERROR: CreateInstance Failed! Error: %d (%#x)\n", hresError, hresError);

                  pcsfFactory->Release();

return hresError;

            }

            pcsfFactory->Release();

      }

//Open Root Handle

      METADATA_HANDLE hMetaDataRoot=NULL;

      METADATA_RECORD mdrRoot;

      HRESULT hRes = pIAdminBase->OpenKey(METADATA_MASTER_ROOT_HANDLE, TEXT("/LM"),

      METADATA_PERMISSION_READ, 20, &hMetaDataRoot);

      BYTE buffer[BUFFER_LEN];

      DWORD dwReturnBufferSize=0;

      CString strStoreName;

      mdrRoot.dwMDAttributes = METADATA_NO_ATTRIBUTES;

      mdrRoot.dwMDUserType = IIS_MD_UT_SERVER;

      mdrRoot.dwMDDataType = ALL_METADATA;

      mdrRoot.dwMDDataLen = BUFFER_LEN;

      mdrRoot.pbMDData = buffer;

      mdrRoot.dwMDIdentifier = MD_SSL_STORE_NAME; // MD_SERVER_COMMENT or so       

      hRes = pIAdminBase->GetData(hMetaDataRoot, L"/W3SVC/1", &mdrRoot, &dwReturnBufferSize);

if (hRes==S_OK)

      {

            strStoreName= (WCHAR*)mdrRoot.pbMDData;

      }

      BYTE binSSLHash[MAX_CERT_SIZE];

      DWORD dwBinHashSize;

      mdrRoot.dwMDAttributes = METADATA_NO_ATTRIBUTES;

      mdrRoot.dwMDUserType = IIS_MD_UT_SERVER;

      mdrRoot.dwMDDataType = BINARY_METADATA;

      mdrRoot.dwMDDataLen = MAX_CERT_SIZE;

      mdrRoot.pbMDData = (unsigned char *)&binSSLHash;

      mdrRoot.dwMDIdentifier = MD_SSL_CERT_HASH ;

//Read the first web site

      hRes = pIAdminBase->GetData(hMetaDataRoot,L"/W3SVC/1", &mdrRoot, &dwBinHashSize);        

if (!SUCCEEDED(hRes))

      {

return hRes;

      }

      dwBinHashSize=mdrRoot.dwMDDataLen;

//CString strHashString= GetHashString(dwBinHashSize,&binSSLHash[0]);

      ReadAndDisplayCertificate(strServerName,strStoreName,dwBinHashSize,&binSSLHash[0]);

return hRes;

}

BOOL ReadAndDisplayCertificate(LPCTSTR lpServerName,LPCTSTR lpStoreName,int nBinHashSize, BYTE *binSSLHash)

{

      HRESULT hr=S_OK;

      HCERTSTORE  hSystemStore=NULL;          // The system store handle.

      PCCERT_CONTEXT  pDesiredCert=NULL ;   // Set to NULL for the first time

      CString strStoreFullPath;

if (lpServerName && lpStoreName)

      {

            strStoreFullPath.Format(L"\\\\%s\\%s",lpServerName,lpStoreName);

      }

else if (lpStoreName)

      {

            strStoreFullPath=lpStoreName;

      }

else

      {

return S_FALSE;

      }

      hSystemStore = CertOpenStore(

                         CERT_STORE_PROV_SYSTEM,

                         0,                      // Encoding type not needed

// with this PROV.

                         NULL,                   // Accept the default HCRYPTPROV.

                         CERT_SYSTEM_STORE_LOCAL_MACHINE,

// Set the system store location in

// the registry.

                         strStoreFullPath);

if (!hSystemStore)

      {

return S_FALSE;

      }

      CRYPT_UINT_BLOB pBlob;

      pBlob.pbData=binSSLHash;

      pBlob.cbData=nBinHashSize;

      pDesiredCert=CertFindCertificateInStore(

                          hSystemStore,

                          (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING),           // Use X509_ASN_ENCODING.

                          0,                          // No dwFlags needed.

                          CERT_FIND_HASH,      // Find a certificate with a     // subject that matches the string   // in the next parameter.

                          &pBlob ,           // The Unicode string to be found        // in a certificate's subject.

                          NULL) ;

if (!pDesiredCert)

return FALSE;

int cbSize = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUER_FLAG,

            NULL,  

            NULL,  

            0);

       CString strName;

       CString strIssuerName;

       CString strIssuedToName;

       TCHAR * pszName =NULL;

if (cbSize>1)

       {

             pszName = strName.GetBuffer( cbSize+1);

             BOOL bConv = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUER_FLAG,

            NULL,  

            pszName,  

            cbSize);

                  strName.ReleaseBuffer();

if (bConv)

             {

                  strIssuerName=strName;

                  printf("Certificate Issued By: %S\n",(LPCTSTR) strIssuerName);

             }

       }

       cbSize = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUEDTO_FLAG,

            NULL,  

            NULL,  

            0);

      pszName =NULL;

if (cbSize>1)

       {

             pszName = strName.GetBuffer( cbSize+1);

             BOOL bConv = CertGetNameString(  

            pDesiredCert,  

            CERT_NAME_SIMPLE_DISPLAY_TYPE,  

            CERT_NAME_ISSUEDTO_FLAG,

            NULL,  

            pszName,  

            cbSize);

                  strName.ReleaseBuffer();

if (bConv)

             {

                  strIssuedToName=strName;                 

                  printf("Certificate Issued To: %S\n",(LPCTSTR) strIssuedToName);

//printf

             }

       }

      CERT_INFO  * pCertInfo = pDesiredCert->pCertInfo;

      SYSTEMTIME st;

      FILETIME lft;  

     FileTimeToLocalFileTime((const FILETIME *)&pCertInfo->NotBefore, &lft);

     FileTimeToSystemTime(( const FILETIME *) &pCertInfo->NotBefore, &st);

       printf(("Certificate Not Valid Before (MM/DD/YY HH:MM) : %02d/%02d/%04d %02d:%02d\n"),

                                            st.wMonth,

                                            st.wDay,

                                            st.wYear,

                                            st.wHour,

                                            st.wMinute);

        FileTimeToSystemTime(( const FILETIME *) &pCertInfo->NotAfter, &st);

            printf(("Certificate Not Valid After (MM/DD/YY HH:MM) : %02d/%02d/%04d %02d:%02d\n"),

                                            st.wMonth,

                                            st.wDay,

                                            st.wYear,

                                            st.wHour,

                                            st.wMinute);

int nValid= CertVerifyTimeValidity(

      NULL,               // Use current time.

      pCertInfo);

      CString strMessage;

switch(nValid)   // Pointer to CERT_INFO.

      {

case -1 :

            {

                  strMessage = (L"Certificate is not valid yet. \n");

break;

            }

case 1:

            {

                  strMessage = (L"Certificate is expired. \n");

break;

            }

case 0:

            {

                  strMessage = (L"Certificate's time is valid. \n");               

break;

            }

      }

      printf("%S\n", (LPCTSTR) strMessage);

return TRUE;

}