Building a Certificate Maintenance Utility

Now, to see how some of this certificate management functionality works, you'll build a utility that lists the certificates and all the properties that can be enumerated. This utility will provide a combo box that lists the primary stock certificate stores from which the user can select. After the user has selected a certificate store, the utility will open the specified certificate store and enumerate all the certificates in that store. It will enumerate all the properties of each certificate. For each property, it will get the current value of the property and display all the information in a big list for the user to see.

Creating the Project

To start this new project, start a new standard EXE project in Visual Basic. Copy the clsCrypto.cls file from the example you built for Chapter 4 into the project directory for this project, and add the file to the project. Next, add the new constants, types, and API function declarations to the clsCrytpo class that are given in Listing 6.1. You can split them up so that the constants are with the other constants, the types are after the constants, and the API function declarations are with the other API function declarations.

Code Listing 6.1. New Constant, Function, and Variable Declarations
'+-----------------------------------------------------------------------
' Certificate name types
'------------------------------------------------------------------------
Private Const CERT_NAME_EMAIL_TYPE = 1
Private Const CERT_NAME_RDN_TYPE = 2
Private Const CERT_NAME_ATTR_TYPE = 3
Private Const CERT_NAME_SIMPLE_DISPLAY_TYPE = 4
Private Const CERT_NAME_FRIENDLY_DISPLAY_TYPE = 5

Private Const CERT_SYSTEM_STORE_MASK = &HFFFF0000

'+-----------------------------------------------------------------------
' Certificate, CRL and CTL property IDs
'
' See CertSetCertificateContextProperty
' or CertGetCertificateContextProperty
' for usage information.
'--------------------------------------------------------------------------
Private Const CERT_KEY_PROV_HANDLE_PROP_ID = 1
Private Const CERT_KEY_PROV_INFO_PROP_ID = 2
Private Const CERT_SHA1_HASH_PROP_ID = 3
Private Const CERT_MD5_HASH_PROP_ID = 4
Private Const CERT_HASH_PROP_ID = CERT_SHA1_HASH_PROP_ID
Private Const CERT_KEY_CONTEXT_PROP_ID = 5
Private Const CERT_KEY_SPEC_PROP_ID = 6
Private Const CERT_IE30_RESERVED_PROP_ID = 7
Private Const CERT_PUBKEY_HASH_RESERVED_PROP_ID = 8
Private Const CERT_ENHKEY_USAGE_PROP_ID = 9
Private Const CERT_CTL_USAGE_PROP_ID = CERT_ENHKEY_USAGE_PROP_ID
Private Const CERT_NEXT_UPDATE_LOCATION_PROP_ID = 10
Private Const CERT_FRIENDLY_NAME_PROP_ID = 11
Private Const CERT_PVK_FILE_PROP_ID = 12
Private Const CERT_DESCRIPTION_PROP_ID = 13
Private Const CERT_ACCESS_STATE_PROP_ID = 14
Private Const CERT_SIGNATURE_HASH_PROP_ID = 15
Private Const CERT_SMART_CARD_DATA_PROP_ID = 16
Private Const CERT_EFS_PROP_ID = 17
Private Const CERT_FORTEZZA_DATA_PROP_ID = 18
Private Const CERT_ARCHIVED_PROP_ID = 19
Private Const CERT_KEY_IDENTIFIER_PROP_ID = 20
Private Const CERT_AUTO_ENROLL_PROP_ID = 21
Private Const CERT_PUBKEY_ALG_PARA_PROP_ID = 22

Private Const CERT_FIRST_RESERVED_PROP_ID = 23
'Note, 32 - 35 are reserved for the CERT, CRL, CTL and
'KeyId file element IDs.

Private Const CERT_LAST_RESERVED_PROP_ID = &H7FFF
Private Const CERT_FIRST_USER_PROP_ID = &H8000
Private Const CERT_LAST_USER_PROP_ID = &HFFFF
Private Type CRYPT_KEY_PROV_PARAM
    dwParam As Long
    pbData As Long
    cbData As Long
    dwFlags As Long
End Type

Private Type CRYPT_KEY_PROV_INFO
    pwszContainerName As Long
    pwszProvName As Long
    dwProvType As Long
    dwFlags As Long
    cProvParam As Long
    rgProvParam As Long
    dwKeySpec As Long
End Type

Private Type CERT_KEY_CONTEXT
    cbSize As Long           'sizeof(CERT_KEY_CONTEXT)
    hCryptProv As Long
    dwKeySpec As Long
End Type

Private Declare Function CertOpenSystemStore Lib "Crypt32.dll" _
    Alias "CertOpenSystemStoreA" (ByVal hProv As Long, _
    ByVal szSubsystemProtocol As String) As Long

Private Declare Function CertEnumCertificatesInStore _
    Lib "Crypt32.dll" ( _
    ByVal hCertStore As Long, ByVal pPrevCertContext As Long) As Long

Private Declare Function CertEnumCertificateContextProperties _
    Lib "Crypt32.dll" (ByVal pCertContext As Long, _
    ByVal dwPropId As Long) As Long

Private Declare Function CertGetCertificateContextProperty _
    Lib "Crypt32.dll" (ByVal pCertContext As Long, _
    ByVal dwPropId As Long, ByVal pvData As String, _
    ByRef pcbData As Long) As Long

Private Declare Function CertGetCertificateContextDWProperty _
    Lib "Crypt32.dll" Alias "CertGetCertificateContextProperty" ( _
    ByVal pCertContext As Long, ByVal dwPropId As Long, _
    ByRef pvData As Long, ByRef pcbData As Long) As Long

Private Declare Function CertGetCertificateContextKCProperty _
    Lib "Crypt32.dll" Alias "CertGetCertificateContextProperty" ( _
    ByVal pCertContext As Long, ByVal dwPropId As Long, _
    ByRef pvData As CERT_KEY_CONTEXT, ByRef pcbData As Long) As Long

Private Declare Function CertGetCertificateContextKPIProperty _
    Lib "Crypt32.dll" Alias "CertGetCertificateContextProperty" ( _
    ByVal pCertContext As Long, ByVal dwPropId As Long, _
    ByRef pvData As CRYPT_KEY_PROV_INFO, ByRef pcbData As Long) As Long

Private Declare Function CertFreeCertificateContext Lib "Crypt32.dll" ( _
    ByVal pCertContext As Long) As Long

Private Declare Function CertCloseStore Lib "Crypt32.dll" ( _
    ByVal hCertStore As Long, ByVal dwFlags As Long) As Long

Private Declare Function CertGetNameString Lib "Crypt32.dll" _
    Alias "CertGetNameStringA" (ByVal pCertContext As Long, _
    ByVal dwType As Long, ByVal dwFlags As Long, _
    ByRef pvTypePara As Long, ByVal pszNameString As String, _
    ByVal cchNameString As Long) As Long
					

Listing the Certificates

Next, you'll add a rather oversized function that lists the certificates and their properties to the crypto class. This function will call a few functions that you'll define after you have hashed out the basic function. This function will first open the specified certificate store. Next, it will enumerate all the certificates in a loop. Within that loop, it will enumerate all the certificate properties within a second loop. Within the second loop, it will determine which version of the CertGetCertificateContextProperty function to use and then use it to get the current value of the property. To add all this functionality, add the ListCerts function in Listing 6.2 to the crypto class.

Code Listing 6.2. The ListCerts Function
Public Function ListCerts(pszStoreName As String) As String
'*************************************************************
'* Written By: Davis Chapman
'* Date:       October 12, 1999
'*
'* Syntax:     ListCerts(pszStoreName)
'*
'* Parameters:  pszStoreName As String
'*
'* Purpose: This will loop through all of the certificates in
'*          the specified store, listing the available
'*          properties of each in turn. It builds this
'*          information as a large text string that is returned
'*          to the calling function to be displayed to the user.
'*************************************************************

    On Error GoTo ListCertsErr

    Dim hCertStore As Long
    Dim pszNameString As String * 256
    Dim pvData As String
    Dim dwData As Long
    Dim kcData As CERT_KEY_CONTEXT
    Dim kpiData As CRYPT_KEY_PROV_INFO
    Dim cbData As Long
    Dim dwPropId As Long
    Dim strMsg As String
    Dim lpCertContext As Long
    Dim szStoreName As String
    Dim szRtnMsg As String
    Dim iPos As Integer
    Dim iVerToUse As Integer

    szRtnMsg = ""
    dwPropId = 0

    '--- Open a system certificate store.
    hCertStore = CertOpenSystemStore(0, pszStoreName)

    '--- Did we open a store?
    If (hCertStore <> 0) Then
        szRtnMsg = "The " + pszStoreName + " store has been opened." + vbCrLf
    Else
        m_sErrorMsg = "Error during CertOpenSystemStore - " & _
            CStr(Err.LastDllError) & Error$
        MsgBox m_sErrorMsg, vbOKOnly, "VB Crypto"
        Exit Function
    End If

    '--- Use CertEnumCertificatesInStore to get the certificates
    '--- from the open store.
    lpCertContext = CertEnumCertificatesInStore(hCertStore, 0)

    '--- Loop until all certificates have been retrieved
    While (lpCertContext <> 0)
        '--- A certificate was retrieved.
        '--- Get the name from the certificate.
        If (CertGetNameString(lpCertContext, _
                CERT_NAME_SIMPLE_DISPLAY_TYPE, _
                0, 0, pszNameString, 128)) Then

            '--- Find the end of the name
            iPos = InStr(1, pszNameString, vbNullChar, vbBinaryCompare)
            '--- Strip off the remaining NULL characters
            If iPos> 0 Then pszNameString = Left(pszNameString, (iPos - 1))
            '--- Tell the user the name of the certificate
            szRtnMsg = szRtnMsg + vbCrLf + "Certificate for " + _
                Trim$(pszNameString) + vbCrLf
        Else
            m_sErrorMsg = "Error during CertGetNameString - " & _
                CStr(Err.LastDllError) & Error$
            MsgBox m_sErrorMsg, vbOKOnly, "VB Crypto"
        End If

        '--- Loop to find all of the property identifiers for the
        '--- specified certificate. The loop continues until
        '--- CertEnumCertificateContextProperties returns zero.
        dwPropId = CertEnumCertificateContextProperties( _
                lpCertContext, dwPropId)
        While (dwPropId <> 0)

            '--- When the loop is executed, a property identifier has
            '--- been found, print the property number.
            strMsg = "Property # " + Str(dwPropId) + " found -> "

            '--- Initialize the property data type indicator
            iVerToUse = 1

            '---  Indicate the kind of property found.
            Select Case dwPropId
                Case CERT_FRIENDLY_NAME_PROP_ID
                    strMsg = strMsg + "Friendly name: "
                    iVerToUse = 5
                Case CERT_SIGNATURE_HASH_PROP_ID
                    strMsg = strMsg + "Signature hash identifier "
                Case CERT_KEY_PROV_HANDLE_PROP_ID
                    strMsg = strMsg + "KEY PROVE HANDLE "
                    iVerToUse = 2
                Case CERT_KEY_PROV_INFO_PROP_ID
                    strMsg = strMsg + "KEY PROV INFO PROP ID "
                    iVerToUse = 4
                Case CERT_SHA1_HASH_PROP_ID
                    strMsg = strMsg + "SHA1 HASH identifier "
                Case CERT_MD5_HASH_PROP_ID
                    strMsg = strMsg + "md5 hash identifier "
                Case CERT_KEY_CONTEXT_PROP_ID
                    strMsg = strMsg + "KEY CONTEXT PROP identifier "
                Case CERT_KEY_SPEC_PROP_ID
                    strMsg = strMsg + "KEY SPEC PROP identifier "
                    iVerToUse = 2
                Case CERT_ENHKEY_USAGE_PROP_ID
                    strMsg = strMsg + "ENHKEY USAGE PROP identifier "
                Case CERT_NEXT_UPDATE_LOCATION_PROP_ID
                    strMsg = strMsg + _ "NEXT UPDATE LOCATION PROP identifier "
                Case CERT_PVK_FILE_PROP_ID
                    strMsg = strMsg + "PVK FILE PROP identifier "
                Case CERT_DESCRIPTION_PROP_ID
                    strMsg = strMsg + "DESCRIPTION PROP identifier "
                    iVerToUse = 5
                Case CERT_ACCESS_STATE_PROP_ID
                    strMsg = strMsg + "ACCESS STATE PROP identifier "
                    iVerToUse = 2
                Case CERT_SMART_CARD_DATA_PROP_ID
                    strMsg = strMsg + "SMART_CARD DATA PROP identifier "
                Case CERT_EFS_PROP_ID
                    strMsg = strMsg + "EFS PROP identifier "
                Case CERT_FORTEZZA_DATA_PROP_ID
                    strMsg = strMsg + "FORTEZZA DATA PROP identifier "
                Case CERT_ARCHIVED_PROP_ID
                    strMsg = strMsg + "ARCHIVED PROP identifier "
                    iVerToUse = 2
                Case CERT_KEY_IDENTIFIER_PROP_ID
                    strMsg = strMsg + "KEY IDENTIFIER PROP identifier "
                Case CERT_AUTO_ENROLL_PROP_ID
                    strMsg = strMsg + "AUTO ENROLL identifier. "
            End Select

            '--- Determine how to retrieve the property value based on the
            '--- property type
            Select Case iVerToUse
                Case 2
                    '--- Set the value size
                    cbData = 4
                    '--- Retrieve the property value
                    If (Not CBool(CertGetCertificateContextDWProperty( _
                            lpCertContext, dwPropId, _
                            dwData, cbData))) Then
                        '--- Unable to retrieve the property value
                        m_sErrorMsg = "Error during " & _
                            "CertGetCertificateContextDWProperty - " & _
                            CStr(Err.LastDllError) & Error$
                        MsgBox m_sErrorMsg, vbOKOnly, "VB Crypto"
                    End If

                    '--- Show the results.
                    strMsg = strMsg + vbCrLf + _
                        "The Property Content is " + Str(dwData)

                Case 3
                    '--- Set the value size
                    cbData = 12
                    '--- Retrieve the property value
                    If (Not CBool(CertGetCertificateContextKCProperty( _
                            lpCertContext, dwPropId, _
                            kcData, cbData))) Then
                        '--- Unable to retrieve the property value
                        m_sErrorMsg = "Error during " & _
                            "CertGetCertificateContextKCProperty - " & _
                            CStr(Err.LastDllError) & Error$
                        MsgBox m_sErrorMsg, vbOKOnly, "VB Crypto"
                    End If
                    '--- Show the results.
                    strMsg = strMsg + vbCrLf + _
                        "The Property Content is " + _
                        Str(kcData.dwKeySpec)

                Case 4
                    '--- Set the value size
                    cbData = 28
                    '--- Retrieve the property value
                    If (CBool(CertGetCertificateContextKPIProperty( _
                            lpCertContext, dwPropId, _
                            kpiData, cbData))) Then
                    End If

                    '--- The call didn't succeed. Use the size to
                    '--- allocate memory for the property.
                    pvData = String(cbData, vbNullChar)

                    '--- Allocation succeeded. Retrieve the property data
                    If (Not CBool(CertGetCertificateContextProperty( _
                            lpCertContext, dwPropId, _
                            pvData, cbData))) Then
                        '--- Unable to retrieve the property value
                        m_sErrorMsg = "Error during " & _
                            "CertGetCertificateContextProperty - " & _
                            CStr(Err.LastDllError) & Error$
                        MsgBox m_sErrorMsg, vbOKOnly, "VB Crypto"
                    End If

                    '--- Show the results.
                    If cbData = 28 Then
                        '--- We retrieved the type structure
                        strMsg = strMsg + vbCrLf + _
                            "The Property Content is " + _
                            Str(kpiData.dwProvType)
                    Else
                        '--- We received a byte array
                        strMsg = strMsg + vbCrLf + _
                            "The Container Name is " + _
                            GetContainerNameFromKPI(pvData)
                        strMsg = strMsg + vbCrLf + "The CSP is " + _
                             GetCSPFromKPI(pvData)
                        strMsg = strMsg + vbCrLf + _
                            "The Provider Type is " + _
                            GetProvTypeFromKPI(pvData)
                        strMsg = strMsg + vbCrLf + _                         "The Key Type
 is " + certificates>
                            GetKeySpecFromKPI(pvData)
                    End If
                Case Else
                    '--- Retrieve information on the property by first
                    '--- getting the size
                    '--- of the property size.
                    cbData = 0
                    If CBool(CertGetCertificateContextProperty( _
                            lpCertContext, dwPropId, _
                            vbNullString, cbData)) Then
                    End If

                    '--- The call succeeded. Use the size to allocate
                    '--- memory for the property.
                    pvData = String(cbData, vbNullChar)

                    '--- Allocation succeeded. Retrieve the property data
                    If (Not CBool(CertGetCertificateContextProperty( _
                            lpCertContext, dwPropId, _
                            pvData, cbData))) Then
                        m_sErrorMsg = "Error during " & _
                            "CertGetCertificateContextProperty - " & _
                            CStr(Err.LastDllError) & Error$
                        MsgBox m_sErrorMsg, vbOKOnly, "VB Crypto"
                    End If

                    '--- Show the results.
                    If iVerToUse = 5 Then
                        '--- The value is a string, extract it from
                        '--- the Byte array
                        strMsg = strMsg + vbCrLf + _
                            "The Property Content is " + _
                            ConvertBytesToString(pvData)
                    Else
                        '--- A byte array, display the ASCII value of
                        '--- the first character
                        strMsg = strMsg + vbCrLf + _
                            "The Property Content is " + Str(Asc(pvData))
                    End If
            End Select

            '--- Add the value to the return string
            szRtnMsg = szRtnMsg + strMsg + vbCrLf

            '--- Get the next property
            dwPropId = CertEnumCertificateContextProperties( _
                lpCertContext, _
                dwPropId)
        Wend

        '--- Get the next certificate
        lpCertContext = CertEnumCertificatesInStore( _
            hCertStore, _
            lpCertContext)
    Wend

    '--- Clean up.
    '--- If we have a current certificate, then release it
    If lpCertContext <> 0 Then
        CertFreeCertificateContext lpCertContext
    End If
    '--- Close the Certificate store
    CertCloseStore hCertStore, 0
    szRtnMsg = szRtnMsg + vbCrLf + "The function completed successfully."
    ListCerts = szRtnMsg
    Exit Function

ListCertsErr:
    MsgBox "Error: " + Str(Err.Number) + " - " + Err.Description
End Function
					

In this function, you first open the certificate store specified in the function parameter:

'--- Open a system certificate store.
hCertStore = CertOpenSystemStore(0, pszStoreName)

By passing zero (0) for the first parameter, you specify that the default CSP should be used for the certificate store.

Next, you start enumerating the certificates in the store, looping until all certificates have been retrieved through the following code:

'--- Use CertEnumCertificatesInStore to get the certificates
'--- from the open store.
lpCertContext = CertEnumCertificatesInStore(hCertStore, 0)

'--- Loop until all certificates have been retrieved
While (lpCertContext <> 0)
    .
    .
    .
    '--- Get the next certificate
    lpCertContext = CertEnumCertificatesInStore( _
        hCertStore, lpCertContext)
Wend

For each certificate that you retrieve, you also retrieve the common subject name by using the CertGetNameString function call:

'--- A certificate was retrieved. Get the name from the certificate.
If (CertGetNameString(lpCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, _
        0, 0, pszNameString, 128)) Then

Next, you loop while enumerating all the properties from the certificate by using the following code:

'--- Loop to find all of the property identifiers for the specified
'--- certificate. The loop continues until
'--- CertEnumCertificateContextProperties returns zero.
dwPropId = CertEnumCertificateContextProperties( _
    lpCertContext, dwPropId)
While (dwPropId <> 0)
    .
    .
    .
    '--- Get the next property
    dwPropId = CertEnumCertificateContextProperties( _
        lpCertContext, dwPropId)
Wend

Within this loop, you use a Select Case statement to determine which property you are getting and which version of the CertGetCertificateContextProperty function to use. The versions of this function are numbered in order, with string properties (for example, names) using a fifth version for reasons that will be discussed next.

At this point, you use another Select Case statement to control which version of the CertGetCertificateContextProperty function is used. Here, you might have noticed an interesting use of the fourth version, which returns a CRYPT_KEY_PROV_INFO type structure, as follows:

'--- Set the value size
cbData = 28
'--- Retrieve the property value
If (CBool(CertGetCertificateContextKPIProperty( _
    lpCertContext, dwPropId, _
    kpiData, cbData))) Then
End If

'--- The call didn't succeed. Use the size to
'--- allocate memory for the property.
pvData = String(cbData, vbNullChar)

'--- Allocation succeeded. Retrieve the property data.
If (Not CBool(CertGetCertificateContextProperty( _
    lpCertContext, dwPropId, _
    pvData, cbData))) Then

If you look back at the type declaration for the CRYPT_KEY_PROV_INFO type, you'll notice that the first two elements in this type structure are pointers to strings. The strings are placed in memory just after the end of the type structure. As a result, you have to allocate a string buffer large enough to hold both the type structure and the strings that are pointed at by the first two elements in the type structure. You'll learn how you can extract these strings without using pointer manipulation a little later.

After you have looped through all the certificates, you free any certificate context that you might still have open as follows:

'--- If we have a current certificate, then release it
If lpCertContext <> 0 Then
    CertFreeCertificateContext lpCertContext
End If

Finally, you close the certificate store by using the following code:

    '--- Close the Certificate store
    CertCloseStore hCertStore, 0
					

Converting Strings

While typing in the ListCerts function, you probably noticed that whenever you were working with a string property of a certificate, you called a function called ConvertBytesToString. You call this function because the string values are returned in Unicode strings, not ASCII strings. As a result, you need to perform a conversion on them. Visual Basic normally does this conversion automatically, but when strings are returned as byte arrays, as is the case in these functions, the strings remain in Unicode.

You can convert the strings from Unicode to ASCII by using the StrConv function. However, because the string variables have been sized larger than the Unicode strings, you might still need to trim some extraneous characters off the end. You can find them by searching for any NULL characters in the string. If a NULL character is found, you can trim the string just shy of the NULL character. You can add all this functionality to your application by entering the ConvertBytesToString function to the crypto class, as in Listing 6.3.

Code Listing 6.3. The ConvertBytesToString Function
Private Function ConvertBytesToString(sbOrg As String) As String
'*************************************************************
'* Written By: Davis Chapman
'* Date:       October 12, 1999
'*
'* Syntax:     ConvertBytesToString(sbOrg)
'*
'* Parameters:  sbOrg As String
'*
'* Purpose: This will convert a byte array value to a string
'*          value by skipping every other character (byte) in
'*          the original string.
'*************************************************************
    Dim strNew As String
    Dim iCurPos As Integer

    '--- Convert the string from Unicode
    strNew = StrConv(sbOrg, vbFromUnicode)
    '--- Find any NULL characters in the string
    iCurPos = InStr(1, strNew, vbNullChar, vbBinaryCompare)
    '--- If any NULL characters were found, trim the string
    '--- one character shy of the NULL character
    If (iCurPos> 0) Then strNew = Left(strNew, (iCurPos - 1))
    '--- Return the extracted string
    ConvertBytesToString = strNew
End Function
					

Extracting the Key Spec

Now that you can convert the string properties to a readable form, you need to turn your attention to interpreting other properties. The first one to look at is the KeySpec property. It can be determined by examining the KeySpec element in the CRYPT_KEY_PROV_INFO type structure. However, because you are retrieving this type as a string, and not as the type itself, you have to convert the four bytes into a long value.

Converting the four bytes into a long variable seems simple enough: You just convert each character into its ASCII value and add them all together one at a time, multiplying the leftmost values by 16 before adding the next byte to the right. The twist to all of this is that this number is stored in little endian format. This means that the bytes go from right to left, not left to right, as you might normally expect. As a result, you have to start with the last character and work your way backward. When you have the numeric value, you can determine which key spec type the certificate is and return a string specifying the type. To build this functionality, add the function in Listing 6.4 to the crypto class.

Code Listing 6.4. The GetKeySpecFromKPI Function
Private Function GetKeySpecFromKPI(sbOrg As String) As String
'*************************************************************
'* Written By: Davis Chapman
'* Date:       October 12, 1999
'*
'* Syntax:     GetKeySpecFromKPI(sbOrg)
'*
'* Parameters:  sbOrg As String
'*
'* Purpose: This will extract the key spec from the
'*          CRYPT_KEY_PROV_INFO type structure.
'*************************************************************
    Dim strCur As String
    Dim strChar As String
    Dim strRtn As String
    Dim iCurPos As Integer
    Dim iRelPos As Integer
    Dim lKeySpec As Long

    '--- Extract the key spec, starting at the
    '--- appropriate offset
    lKeySpec = Asc(Mid(sbOrg, 28, 1))
    lKeySpec = (lKeySpec * 16) + Asc(Mid(sbOrg, 27, 1))
    lKeySpec = (lKeySpec * 16) + Asc(Mid(sbOrg, 26, 1))
    lKeySpec = (lKeySpec * 16) + Asc(Mid(sbOrg, 25, 1))
    '--- Determine the key spec
    Select Case lKeySpec
        Case AT_KEYEXCHANGE
            strRtn = "AT_KEYEXCHANGE"
        Case AT_SIGNATURE
            strRtn = "AT_SIGNATURE"
    End Select
    '--- Return the key spec
    GetKeySpecFromKPI = strRtn
End Function
					

Extracting the Provider Type

Next, you'll add a function to extract the CSP provider type from the CRYPT_KEY_PROV_INFO type structure. This works the same as how you extracted the key spec from the string that contains this type structure. After the provider type value has been extracted and converted, you can determine which provider type it is and return a string description to the calling routine. To add this functionality, add the code in Listing 6.5 to the crypto class.

Code Listing 6.5. The GetProvTypeFromKPI Function
Private Function GetProvTypeFromKPI(sbOrg As String) As String
'*************************************************************
'* Written By: Davis Chapman
'* Date:       October 12, 1999
'*
'* Syntax:     GetProvTypeFromKPI(sbOrg)
'*
'* Parameters:  sbOrg As String
'*
'* Purpose: This will extract the provider type from the
'*          CRYPT_KEY_PROV_INFO type structure.
'*************************************************************
    Dim strCur As String
    Dim strChar As String
    Dim strRtn As String
    Dim iCurPos As Integer
    Dim iRelPos As Integer
    Dim lProvType As Long

    '--- Extract the provider type, starting at the
    '--- appropriate offset
    lProvType = Asc(Mid(sbOrg, 12, 1))
    lProvType = (lProvType * 16) + Asc(Mid(sbOrg, 11, 1))
    lProvType = (lProvType * 16) + Asc(Mid(sbOrg, 10, 1))
    lProvType = (lProvType * 16) + Asc(Mid(sbOrg, 9, 1))
    '--- Determine the provider type
    Select Case lProvType
        Case PROV_RSA_FULL
            strRtn = "PROV_RSA_FULL"
        Case PROV_RSA_SIG
            strRtn = "PROV_RSA_SIG"
        Case PROV_DSS
            strRtn = "PROV_DSS"
        Case PROV_FORTEZZA
            strRtn = "PROV_FORTEZZA"
        Case PROV_MS_EXCHANGE
            strRtn = "PROV_MS_EXCHANGE"
        Case PROV_SSL
            strRtn = "PROV_SSL"
        Case PROV_RSA_SCHANNEL
            strRtn = "PROV_RSA_SCHANNEL"
        Case PROV_DSS_DH
            strRtn = "PROV_DSS_DH"
    End Select
    '--- Return the provider type
    GetProvTypeFromKPI = strRtn
End Function
					

Extracting the Container Name

Now it's time to turn your attention back to extracting the string values from the CRYPT_KEY_PROV_INFO type structure. Because these values are included in the return string, just after the type structure, you can extract them from that point on. The first string that you'll extract is the container name. It starts just after the type structure. Because the CRYPT_KEY_PROV_INFO type structure is 28 bytes long, you can extract everything from the twenty-ninth character on and pass it to the ConvertBytesToString function to extract the container name. You do so by adding the code in Listing 6.6 to the crypto class.

Code Listing 6.6. The GetContainerNameFromKPI Function
Private Function GetContainerNameFromKPI(sbOrg As String) As String
'*************************************************************
'* Written By: Davis Chapman
'* Date:       October 12, 1999
'*
'* Syntax:     GetContainerNameFromKPI(sbOrg)
'*
'* Parameters:  sbOrg As String
'*
'* Purpose: This will extract the container name from the
'*          CRYPT_KEY_PROV_INFO type structure.
'*************************************************************
    Dim strCur As String
    Dim strChar As String
    Dim strRtn As String
    Dim iCurPos As Integer
    Dim iRelPos As Integer

    '--- Extract the container name, starting at the
    '--- appropriate offset
    strRtn = ConvertBytesToString(Mid(sbOrg, 29))
    '--- Return the container name
    GetContainerNameFromKPI = strRtn
End Function
					

Extracting the CSP Name

Extracting the CSP name is a little more complicated. First, it is after the container name in the string buffer returned with the CRYPT_KEY_PROV_INFO type structure. Plus, four extra bytes are inserted between the two strings (which convert to two extra characters after converting from Unicode). As a result, you can't just pass the string to the ConvertBytesToString function, but instead you must perform the conversion yourself.

After you have converted the entire string section from Unicode to ASCII, you can find the end of the container name by searching for the first NULL character. When you find this point, trim off everything through two more characters from the first of the string. This leaves you at the start of the CSP name. Now you can search for any additional NULL characters that mark the end of the CSP name and trim the end of the string at that point. To add this functionality, add the code in Listing 6.7 to the crypto class.

Code Listing 6.7. The GetCSPFromKPI Function
Private Function GetCSPFromKPI(sbOrg As String) As String
'*************************************************************
'* Written By: Davis Chapman
'* Date:       October 12, 1999
'*
'* Syntax:     GetCSPFromKPI(sbOrg)
'*
'* Parameters:  sbOrg As String
'*
'* Purpose: This will extract the CSP from the CRYPT_KEY_PROV_INFO
'*          type structure.
'*************************************************************
    Dim strChar As String
    Dim strRtn As String
    Dim iCurPos As Integer

    '--- Initialize the starting position
    iCurPos = 29
    '--- Trim off the CRYPT_KEY_PROV_INFO structure
    strChar = Mid(sbOrg, iCurPos)
    '--- Convert the string
    strRtn = StrConv(strChar, vbFromUnicode)
    '--- Find the end of the first string
    iCurPos = InStr(1, strRtn, vbNullChar, vbBinaryCompare)
    '--- Trim off the first string
    If (iCurPos> 0) Then strRtn = Mid(strRtn, (iCurPos + 3))
    '--- Find the end of the second string
    iCurPos = InStr(1, strRtn, vbNullChar, vbBinaryCompare)
    '--- Trim the string one character shy of the NULL character
    If (iCurPos> 0) Then strRtn = Left(strRtn, (iCurPos - 1))
    '--- Return the CSP name
    GetCSPFromKPI = strRtn
End Function
					

Designing the Form

At this point, you've added all the necessary new functionality to the crypto class, so it's time to turn your attention back to the form portion of the application. Add controls to the default form as shown in Figure 6.1, setting their properties as specified in Table 6.13.

Figure 6.1. Designing the application form.


Table 6.13. Control Property Settings
Control Property Value
Combo Box Name cboStore
 List MY
  ROOT
  CA
  SPC
  TRUST
Command Button Name cmdList
 Caption &List Certificates
Command Button Name cmdExit
 Caption E&xit
TextBox Name txtCerts
 Multiline True

Next, attach the code in Listing 6.8 to the Click event of the Exit button to close the application.

Code Listing 6.8. The cmdExit Click Event
Private Sub cmdExit_Click()
    '--- Exit the application
    Unload Me
End Sub
					

Listing the Certificates

Now, to perform the listing of certificates and their properties, call the ListCerts function of the crypto class with the name of the certificate store that the user has selected from the combo box, passing the returned string to the text box for the user to view. Do this in the Click event of the cmdList command button, as in Listing 6.9.

Code Listing 6.9. The cmdList Click Event
Private Sub cmdList_Click()
    Dim csCrypt As New clsCrypto

    '--- Retrieve and display the list of certificates
    txtCerts = csCrypt.ListCerts(cboStore)
End Sub

At this point, you should be able to run this application on your system, viewing the certificates you have stored on your computer, as shown in Figure 6.2.

Figure 6.2. Viewing certificate properties.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset