Building a Certificate Request Utility

To get a better understanding of how these two COM objects work, you'll build a Certificate Request utility. This utility will perform the following steps:

  1. Collect identification information from the user

  2. Allow the user to specify how the certificate will be used

  3. Build the certificate request and send it to the CA

  4. Allow the user to check the status of the request

  5. After the certificate has been issued, retrieve the certificate and accept it into the certificate store

Tip

Because of the nature of this example, trying this utility against a local copy of Microsoft's Certificate Server would be a good idea. This way, you can issue and deny certificate requests that are generated by this utility.


Creating the Certificate Request Class

You start your project by creating a new, standard executable Visual Basic project. After you've created the application shell and default form, you can turn your attention to the Certificate Request functionality. First, though, you need to include a reference to the CertCli Type Library in the Project, References dialog, as shown in Figure 5.3.

Figure 5.3. Including the Certificate Request object in the project.


Next, instead of extending the crypto class that you have been building over the last few chapters, you'll create a new class with just certificate request functionality in it. To get started, create a new class, name it clsCertRequest, and add the constant and type declarations in Listing 5.1.

Code Listing 5.1. The clsCertRequest Class Declarations
Option Explicit

'--- Certificate Request Format
Const CR_IN_BASE64HEADER = 0
Const CR_IN_BASE64 = &H1
Const CR_IN_BINARY = &H2
Const CR_IN_ENCODEMASK = &HFF
Const CR_IN_PKCS10 = &H100
Const CR_IN_KEYGEN = &H200
Const CR_IN_FORMATMASK = &HFF00
Const CR_IN_ENCRYPTED_REQUEST = &H10000
Const CR_IN_ENCRYPTED_ATTRIBUTES = &H20000

'--- Certificate Request Status
Const CR_DISP_INCOMPLETE = 0
Const CR_DISP_ERROR = &H1
Const CR_DISP_DENIED = &H2
Const CR_DISP_ISSUED = &H3
Const CR_DISP_ISSUED_OUT_OF_BAND = &H4
Const CR_DISP_UNDER_SUBMISSION = &H5

'--- Issued Certificate Format
Const CR_OUT_BASE64HEADER = 0
						
						
						
						
						
						
Const CR_OUT_BASE64 = &H1
Const CR_OUT_BINARY = &H2
Const CR_OUT_ENCODEMASK = &HFF
Const CR_OUT_CHAIN = &H100

'--- Key Types
Const AT_KEYEXCHANGE = 1
Const AT_SIGNATURE = 2

Public Enum enmKeyType
    KT_KeyExchange = AT_KEYEXCHANGE
    KT_Signature = AT_SIGNATURE
End Enum

'-------------------------------------------------------------------
' Enhanced Key Usage (Purpose) Object Identifiers
'-------------------------------------------------------------------
Const szOID_PKIX_KP = "1.3.6.1.5.5.7.3"

'--- Consistent key usage bits: DIGITAL_SIGNATURE, KEY_ENCIPHERMENT
'--- or KEY_AGREEMENT
Const szOID_PKIX_KP_SERVER_AUTH = "1.3.6.1.5.5.7.3.1"

'--- Consistent key usage bits: DIGITAL_SIGNATURE
Const szOID_PKIX_KP_CLIENT_AUTH = "1.3.6.1.5.5.7.3.2"

'--- Consistent key usage bits: DIGITAL_SIGNATURE
Const szOID_PKIX_KP_CODE_SIGNING = "1.3.6.1.5.5.7.3.3"
 '--- Consistent key usage bits: DIGITAL_SIGNATURE, NON_REPUDIATION
'--- and/or (KEY_ENCIPHERMENT or KEY_AGREEMENT)
Const szOID_PKIX_KP_EMAIL_PROTECTION = "1.3.6.1.5.5.7.3.4"

Public Enum enmKeyUsage
    KU_ClientAuthentication = 0
    KU_EMailProtection = 1
    KU_ServerAuthentication = 2
    KU_CodeSigning = 3
End Enum

'-------------------------------------------------------------------
' Microsoft Enhanced Key Usage (Purpose) Object Identifiers
'-------------------------------------------------------------------

'---  Signer of CTLs
Const szOID_KP_CTL_USAGE_SIGNING = "1.3.6.1.4.1.311.10.3.1"

'---  Signer of TimeStamps
						
						
						
						
						
						
Const szOID_KP_TIME_STAMP_SIGNING = "1.3.6.1.4.1.311.10.3.2"

'-------------------------------------------------------------------
' Microsoft Attribute Object Identifiers
'-------------------------------------------------------------------
Const szOID_YESNO_TRUST_ATTR = "1.3.6.1.4.1.311.10.4.1"

'-- Properties
Private m_strUserName As String
Private m_strUnit As String
Private m_strOrganization As String
Private m_strCity As String
Private m_strState As String
Private m_strCountry As String
Private m_lReqID As Long
Private m_strServer As String
Private m_strCA As String
Private m_strEMail As String
Private m_lKeySpec As enmKeyType
Private m_strKeyUsage As String
Private m_lKeyUsage As enmKeyUsage

'-- Objects for Enrollment and Request controls
Private m_objXen As Object
Private m_objReq As CCertRequest
						
						
						
						
						
						
					

Creating the Class Properties

While going through all the class declarations, you might have noticed a whole string of property variables. They hold pieces of information you need to include with the certificate request and hold the request ID. Next, you need to expose all these properties, as in Listing 5.2.

Code Listing 5.2. clsCertRequest Class Properties
Public Property Get UserName() As String
    UserName = m_strUserName
End Property

Public Property Let UserName(ByVal sNewValue As String)
    m_strUserName = sNewValue
End Property

Public Property Get EMail() As String
    EMail = m_strEMail
End Property

Public Property Let EMail(ByVal sNewValue As String)
    m_strEMail = sNewValue
End Property

Public Property Get Unit() As String
    Unit = m_strUnit
End Property

Public Property Let Unit(ByVal sNewValue As String)
    m_strUnit = sNewValue
End Property

Public Property Get Organization() As String
    Organization = m_strOrganization
End Property

Public Property Let Organization(ByVal sNewValue As String)
    m_strOrganization = sNewValue
End Property

Public Property Get City() As String
    City = m_strCity
End Property

Public Property Let City(ByVal sNewValue As String)
    m_strCity = sNewValue
						
						
						
						
						
						
						
						
						
End Property
Public Property Get State() As String
    State = m_strState
End Property

Public Property Let State(ByVal sNewValue As String)
    m_strState = sNewValue
End Property

Public Property Get Country() As String
    Country = m_strCountry
End Property

Public Property Let Country(ByVal sNewValue As String)
    m_strCountry = sNewValue
End Property

Public Property Get RequestID() As Long
    RequestID = m_lReqID
End Property

Public Property Get Server() As String
    Server = m_strServer
End Property

Public Property Let Server(ByVal sNewValue As String)
    m_strServer = sNewValue
End Property

Public Property Get CertificateAuthority() As String
    CertificateAuthority = m_strCA
End Property

Public Property Let CertificateAuthority(ByVal sNewValue As String)
    m_strCA = sNewValue
End Property

Public Property Get KeySpec() As enmKeyType
    KeySpec = m_lKeySpec
End Property

Public Property Let KeySpec(ByVal lNewValue As enmKeyType)
    m_lKeySpec = lNewValue
End Property

Public Property Get KeyUsage() As enmKeyUsage
						
    KeyUsage = m_lKeyUsage
						
						
						
						
						
						
						
End Property
Public Property Let KeyUsage(ByVal lNewValue As enmKeyUsage)
    '--- Set the key usage value
    m_lKeyUsage = lNewValue

    '--- Set the Key Usage OID to the appropriate string
    Select Case m_lKeyUsage
        Case KU_ClientAuthentication
            m_strKeyUsage = szOID_PKIX_KP_CLIENT_AUTH
        Case KU_EMailProtection
            m_strKeyUsage = szOID_PKIX_KP_EMAIL_PROTECTION
        Case KU_ServerAuthentication
            m_strKeyUsage = szOID_PKIX_KP_SERVER_AUTH
        Case KU_CodeSigning
            m_strKeyUsage = szOID_PKIX_KP_CODE_SIGNING
    End Select
End Property
						
					

In the KeyUsage property in Listing 5.2, notice that you set not only the variable holding the enumerated value, but also a string value to the OID associated with the specified key usage. You use constants that you declared in Listing 5.1 for these OID values. It's important that these OID values are correct, so it doesn't hurt to go back and review them for correctness.

Class Initialization and Termination

Upon creating this class, you need to create an instance of both the Certificate Enrollment Control and the Certificate Request object. You can keep these instances alive for the life of the class. You create the Certificate Enrollment Control by using the CreateObject function, whereas the Certificate Request object can be instantiated with the New keyword, as in Listing 5.3.

Code Listing 5.3. clsCertRequest Class Initialization
Private Sub Class_Initialize()
'*************************************************************
'* Written By: Davis Chapman
'* Date:       September 12, 1999
'*
'* Syntax:     Class_Initialize
'*
'* Parameters:  None
'*
'* Purpose: During the class initialization, the instances of
'*          the Certificate Enroll and Certificate Request
'*          objects are created.
'*************************************************************
    On Error GoTo InitErr
    m_lReqID = 0

    '--- Instantiate the certificate enrollment object
    Set m_objXen = CreateObject("CEnroll.CEnroll.1")

    '--- Instantiate the certificate request object
    Set m_objReq = New CCertRequest

    Exit Sub
InitErr:
    '--- Tell the user about the error
    MsgBox "Error creating request objects - " + Str(Err.Number) + _
            " - " + Err.Description
End Sub
						
						
						
						
						
						
						
						
						
					

When the class is being terminated, you'll want to free both of these resources by setting their variables to Nothing, as in Listing 5.4.

Code Listing 5.4. clsCertRequest Class Termination
Private Sub Class_Terminate()
    '--- Free the objects
    Set m_objXen = Nothing
    Set m_objReq = Nothing
End Sub
						
						
						
						
						
						
						
						
						
					

Requesting Certificates

Now it's time to get down to action. The first real piece of meaty functionality that you can add to the class is the part that creates and submits the certificate request. In this functionality, you can build the distinguished names string from the information the user provides. Next, you can specify the key type that you want to generate and create a certificate request. When you have a certificate request, you can submit it to the CA and display the request status for the user. Once the request has been submitted, you can get the request ID for use at a later time. Finally, just for good measure, you can add some code that, if by some slim chance, the certificate was issued immediately, you can go ahead and get the certificate. To add all this functionality, add the code in Listing 5.5.

Code Listing 5.5. The DoCertRequest Function
Public Function DoCertRequest() As Boolean
'*************************************************************
'* Written By: Davis Chapman
'* Date:       September 12, 1999
'*
'* Syntax:     DoCertRequest
'*
'* Parameters:  None
'*
'* Purpose: This function builds the distinguished name string
'*          and then calls the XEnroll control to generate a
'*          public/private key pair and export the public key
'*          in the form of a certificate request. The certificate
'*          request is then submitted to the Certificate Authority
'*          using the Certificate Request control.
'*************************************************************
    DoCertRequest = False

    Dim strDN As String
    Dim strReq As String
    Dim lDisp As Long
    Dim strMsg As String

    On Error GoTo DoCertReqErr

    '--- Build the distinguished name string
    strDN = "CN=" + m_strUserName _
        + ",OU=" + m_strUnit _
        + ",O=" + m_strOrganization _
						
						
						
						
						
						
        + ",L=" + m_strCity _
        + ",S=" + m_strState _
        + ",C=" + m_strCountry

    '--- Do we have an E-Mail address?
    If (m_strEMail <> "") Then
        '--- Yes, so include it also
        strDN = strDN + ",E=" + m_strEMail
    End If

    '--- Specify the key type
    m_objXen.KeySpec = m_lKeySpec

    '--- Create the PKCS #10 request
						
    strReq = m_objXen.createPKCS10(strDN, m_strKeyUsage)
    '--- Submit the request to the CA
    lDisp = m_objReq.Submit(CR_IN_BASE64 Or CR_IN_PKCS10, _
                            strReq, _
                            "", _
                            m_strServer + "" + m_strCA)

    '--- Get the textual description of the request status
    strMsg = m_objReq.GetDispositionMessage
    '--- Display it for the user
    MsgBox strMsg, vbOKOnly, "Certificate Request"

    '--- Did we fail? If so, then exit early
    If ((lDisp = CR_DISP_INCOMPLETE) Or _
        (lDisp = CR_DISP_ERROR) Or _
        (lDisp = CR_DISP_DENIED)) Then Exit Function

    '--- Get the request ID
    m_lReqID = m_objReq.GetRequestId

    '--- If the certificate has already been issued,
    '--- then get the certificate
    If (lDisp = CR_DISP_ISSUED) Or _
            (lDisp = CR_DISP_ISSUED_OUT_OF_BAND) Then
        If Not GetCert Then Exit Function
    End If

    '--- Didn't exit early, return TRUE
    DoCertRequest = True
    Exit Function

DoCertReqErr:
    '--- Tell the user about the error
    MsgBox "Error - " + Str(Err.Number) + " - " + Err.Description

End Function
						
						
						
						
						
						
						
						
					

Retrieving Certificates

The next piece of functionality that you can add to your class is the retrieval of the issued certificate. In this function, you'll get the certificate from the server and then accept it into the certificate store. To add this functionality, add the code in Listing 5.6 to the clsCertRequest class.

Code Listing 5.6. The GetCert Function
Public Function GetCert() As Boolean
'*************************************************************
'* Written By: Davis Chapman
'* Date:       September 12, 1999
'*
'* Syntax:     GetCert
'*
'* Parameters:  None
'*
'* Purpose: This function retrieves the issued certificate from
'*          the CA using the Certificate Request control. Next,
'*          the Certificate Enroll control is used to accept the
'*          certificate and to place it into the certificate
'*          store.
'*************************************************************
    On Error GoTo GetCertErr
    Dim strCert As String

    '--- Initialize the return value
    GetCert = False

    '--- Get the certificate
    strCert = m_objReq.GetCertificate(CR_OUT_BINARY Or CR_OUT_CHAIN)

    '--- Accept the certificate into the certificate store
    m_objXen.acceptPKCS7 strCert

    '--- Didn't exit early, return TRUE
    GetCert = True

    Exit Function

GetCertErr:
    '--- Tell the user about the error
    MsgBox "Error - " + Str(Err.Number) + " - " + Err.Description
End Function
						
						
						
						
						
						
						
						
					

Checking on Request Status

The final piece of functionality to be added to the clsCertRequest class is the checking of the request status. In this function, you'll need to retrieve the current status from the CA and display it for the user. If the certificate has already been issued, you'll call the GetCert function to retrieve the certificate. To add this functionality to the clsCertRequest class, add the code in Listing 5.7.

Code Listing 5.7. The GetReqStatus Function
Public Function GetReqStatus() As Long
'*************************************************************
'* Written By: Davis Chapman
'* Date:       September 12, 1999
'*
'* Syntax:     GetReqStatus
'*
'* Parameters:  None
'*
'* Purpose: This function checks the current status of the
'*          current outstanding certificate request. If the
'*          request has been issued, then the certificate is
'*          retrieved.
'*************************************************************
    Dim lDisp As Long
    Dim strMsg As String

    '--- Retrieve the current status of our request
    lDisp = m_objReq.RetrievePending(m_lReqID, _
            m_strServer + "" + m_strCA)

    '--- Get the textual description of the request status
    strMsg = m_objReq.GetDispositionMessage
    '--- Display it for the user
    MsgBox strMsg, vbOKOnly, "Certificate Request"

    '--- Set the status code to the return value
    GetReqStatus = lDisp

    '--- If the certificate has already been issued,
    '--- then get the certificate
    If (lDisp = CR_DISP_ISSUED) Or _
            (lDisp = CR_DISP_ISSUED_OUT_OF_BAND) Then
        If Not GetCert Then Exit Function
    End If
End Function
						
						
						
						
						
						
						
						
					

Designing the Form

Now it's time to turn your attention back to the application form. You need to gather a lot of information from the user of this utility, so you can add controls to the form as shown in Figure 5.4, setting their properties as specified in Table 5.10.

Figure 5.4. Designing the application form.


Table 5.10. Control Property Settings
Control Property Value
Label Caption Ser&ver Name:
TextBox Name txtServer
Label Caption Certi&ficate Authority:
TextBox Name txtCA
Label Caption User &Name:
TextBox Name txtUserName
Label Caption Business &Unit:
TextBox Name txtUnit
Label Caption &Organization:
TextBox Name txtOrganization
Label Caption E-&Mail:
TextBox Name txtEMail
Label Caption &City:
TextBox Name txtCity
Label Caption &State:
TextBox Name txtState
Label Caption Countr&y:
TextBox Name txtCountry
Frame (Group Box) Caption Key Type
OptionButton Name optKeyExchange
  Caption Key &Exchange
OptionButton Name optSignature
  Caption Signa&ture
Frame (Group Box) Caption Key Usage
OptionButton Name optCliAuth
  Caption Client &Authentication
OptionButton Name optEMailProt
  Caption E-Mail &Protection
OptionButton Name optServAuth
  Caption Server Authent&ication
OptionButton Name optCodeSign
  Caption Co&de Signing
Command Button Name cmdReqCert
  Caption &Request Certificate
Command Button Name cmdCheckStat
  Caption Chec&k Status
Command Button Name cmdGetCert
  Caption &Get Certificate
Command Button Name cmdExit
  Caption E&xit

Form Initialization and Shutdown

In this application, it is easiest if you create a form-level instance of the Certificate Request class. To do so, add the code in Listing 5.8 to the form declaration section.

Code Listing 5.8. Form Declarations
Option Explicit

Private crRequest As clsCertRequest
						
					

So that this instance of the Certificate Request class will be available throughout the life of the application, create it in the form load event, as in Listing 5.9.

Code Listing 5.9. The Form Load Event
Private Sub Form_Load()
    '--- Create the Certificate Request object
    Set crRequest = New clsCertRequest
End Sub
						
					

To clean up after yourself, you should destroy the Certificate Request class during the form unload event, as in Listing 5.10.

Code Listing 5.10. Form Termination
Private Sub Form_Unload(Cancel As Integer)
    '--- Destroy the Certificate Request object
    Set crRequest = Nothing
End Sub

To add the final piece of housekeeping code for the form, you need to close the application from the Exit button. To do so, add the code in Listing 5.11 to the Click event of the cmdExit button.

Code Listing 5.11. The cmdExit Click Event
Private Sub cmdExit_Click()
    '--- Close this application
    Unload Me
End Sub
						
						
						
						
						
						
						
					

Performing the Certificate Request

Now you're ready to make the application submit a certificate request. You need to copy all the information the user has entered to the appropriate properties of the Certificate Request class. After all the information has been copied, you can call the class'DoCertRequest method, as in Listing 5.12.

Code Listing 5.12. The DoCertReq Subroutine
Private Sub DoCertReq()
'*************************************************************
'* Written By: Davis Chapman
'* Date:       September 12, 1999
'*
'* Syntax:     DoCertReq
'*
'* Parameters:  None
'*
'* Purpose: This subroutine sets the values entered by the user
'*          to the appropriate properties on the Certificate
'*          Request object. Next, it calls the DoCertRequest
'*          method in the class to make the certificate request.
'*************************************************************

    '--- Set the certificate properties
    crRequest.UserName = txtUserName
    crRequest.Unit = txtUnit
    crRequest.Organization = txtOrganization
    crRequest.City = txtCity
    crRequest.State = txtState
    crRequest.Country = txtCountry
    crRequest.EMail = txtEMail

    '--- Specify what Certificate Authority to use
    crRequest.Server = txtServer
    crRequest.CertificateAuthority = txtCA

    '--- Specify how the certificate and key will be used
    If optSignature Then
        crRequest.KeySpec = KT_Signature
    Else
        crRequest.KeySpec = KT_KeyExchange
    End If
    Select Case True
        Case optCliAuth
            crRequest.KeyUsage = KU_ClientAuthentication
        Case optEMailProt
            crRequest.KeyUsage = KU_EMailProtect
						
						
						
						ion
        Case optServAuth
            crRequest.KeyUsage = KU_ServerAuthentication
        Case optCodeSign
            crRequest.KeyUsage = KU_CodeSigning
						
    End Select

    '--- Request the certificate
    If crRequest.DoCertRequest Then
    End If
End Sub

Now that you have the functionality to request the certificate, you need to trigger it from the Click event of the cmdReqCert button. To do so, add the code in Listing 5.13 to this event.

Code Listing 5.13. The cmdReqCert Click Event
Private Sub cmdReqCert_Click()
    '--- Request a certificate
    DoCertReq
End Sub
						
						
						
						
					

Checking the Status and Getting the Certificate

The rest of the form functionality is fairly straightforward. You need to check the status of the request from the Click event of the cmdCheckStat button. This is a simple matter of calling the Certificate Request class'GetReqStatus method, as in Listing 5.14.

Code Listing 5.14. The cmdCheckStat Click Event
Private Sub cmdCheckStat_Click()
    '--- Check the status of our certificate request
    crRequest.GetReqStatus
End Sub

The final piece of functionality is to retrieve the certificate from the cmdGetCert button. Here, you need to call the class'GetCert method from the Click event, as in Listing 5.15.

Code Listing 5.15. The cmdGetCert Click Event
Private Sub cmdGetCert_Click()
    '--- Get the certificate
    crRequest.GetCert
End Sub
						
						
						
						
						
						
						
						
					

Running the Sample Application

Running this sample application requires having access to Certificate Server. You need to be able to issue the certificate fairly quickly because you didn't build in any functionality to maintain the certificate request ID across sessions. Therefore, you have to keep the application running until the certificate has been issued.

When you first start the application, you should fill in all the information on the form. Remember that the country code can be only two characters long. In the first text box, you enter the network name of the computer that has Certificate Server running on it. The second text box is the name of the Certificate Authority as configured in Certificate Server. Fill in the rest of the information as you see fit, as shown in Figure 5.5.

Figure 5.5. Running the sample application.


After you submit the certificate request to the server (assuming that you encountered no errors), you need to open the Certification Authority utility on the Certificate Server machine. This utility manages the certificates issued by Certificate Server. In this utility, there should be a folder containing certificate requests, and your certificate request should be located in this list. Find it and approve it (this process is also known as resubmitting it), as shown in Figure 5.6. In Certificate Server 2 (shipped with Windows 2000), you can resubmit it by right-clicking over your certificate and selecting Resubmit from the list of tasks.

Figure 5.6. Issuing the certificate.


After the certificate has been issued, you can go back to your application and check the current status of the certificate. This action should automatically retrieve the certificate, placing it into your certificate store. To check, you can open the Properties dialog for Internet Explorer and then open the Certificates dialog from the Content tab. In the Certificates dialog, you should be able to find your new certificate in the Personal Certificates folder, as shown in Figure 5.7.

Figure 5.7. Viewing the certificate.


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

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