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.
Table 6.13. Control Property SettingsControl
| 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.