Chapter 7: Issuing Certificates

Contents

7.1 Setting up Your Own CA

A certificate must be issued by a certification authority (CA) that everyone trusts. There are several companies out there that issue certificates, such as InstantSSL, VeriSign or Thawte. In an enterprise environment, however, you may choose to set up your own certification authority to issue personal certificates to be used internally by your company employees. You can also issue your own SSL certifictates for your intranet Web servers.

AspEncrypt can be used to set up your own CA as it offers various certificate-generation and management functions.

7.2 Certification Authority Hierarchy

AspEncrypt creates certificates using the CryptoContext methods CreateCertificate and CreateCertificateFromRequest. The former accepts a person's information in the form of a list of tagged values (as shown below). The latter accepts a Certificate Request File in the PKCS#10 format as the input. Both methods accept a signer context as the first argument.

To construct a hierarchy of certification authorities you must start by creating a self-signed certificate which will become the root of the hierarchy. The following code sample creates a self-signed certificate and places it in the ROOT store via CryptoStore's AddCertificate method.

Note that you should not attempt to add a certificate to the HKEY_CURRENT_USER-based ROOT store in an ASP/ASP.NET environment as this may hang up your web server.

Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContext("RootContainer##2048", True )

' Subject is a CR/LF separated list of tagged values
Subject = "CN=Acme Software Root CA" & chr(13) & chr(10)
Subject = Subject & "C=US" & chr(13) & chr(10)
Subject = Subject & "L=New York" & chr(13) & chr(10)
Subject = Subject & "S=NY" & chr(13) & chr(10)

' Create self-signed certificate valid for 5 years starting now
' The last argument specifies that the private key must be included when saved.
Set Cert = Context.CreateCertificate(Nothing, Subject, Now(), Now() + 5*365, True)

' Save to HKEY_LOCAL_MACHINE-based ROOT store.
CM.LogonUser "domain", "account", "password"
Set Store = CM.OpenStore( "ROOT", True ) ' DO NOT TRY False!
Store.AddCertificate Cert

Response.Write Cert.SerialNumber
ICryptoManager objCM = new CryptoManager();

ICryptoContext objContext =
objCM.OpenContext("RootContainer2##2048", true, Missing.Value );

// Subject is a CR/LF separated list of tagged values
String strSubject = "CN=Acme Software Root CA\r\n";
strSubject += "C=US\r\n";
strSubject += "L=New York\r\n";
strSubject += "S=NY\r\n";

// Create self-signed certificate valid for 5 years starting now
// The last argument specifies that the private key must be included when saved.
ICryptoCert objCert = objContext.CreateCertificate( null,
   strSubject, DateTime.Now, DateTime.Now.AddYears(5), true );

// Save to HKEY_LOCAL_MACHINE-based ROOT store.
objCM.LogonUser( "domain", "account", "password", Missing.Value );
ICryptoStore objStore = objCM.OpenStore( "ROOT", true ); // DO NOT TRY false!
objStore.AddCertificate( objCert, Missing.Value );

txtResult.Text = objCert.SerialNumber;

Here, we pass "RootContainer##2048" to OpenContext to specify a 2048-bit public key for our root certificate instead of the default 1024-bit one. You will notice a delay in executing this script as generating a 2048-bit public key takes much computing power.

Note also that we specified Nothing (null) as the first argument to CreateCertificate to create a self-signed certificate. The third and fourth arguments specify the validity period for this certificate (in this case 5 years starting now.) The last argument specifies whether the certificate's private key should be bundled with the certificate when the latter is saved in a store.

Finally, the script outputs the serial number of the newly created certificate. Record this number as you will need it for the certificate-signing scripts in the following section.

Before running this script, replace the arguments to the LogonUser method with a valid domain/username/password combination.

7.3 Creating a CA-signed Certificate

A self-signed certificate created by the code above can be used to sign individual client certificates directly. You may instead use this certificate to sign other certificates that would serve as sub-authorities in your certificate authority hierarchy. For example, you may set up sub-authorities for each of your organization's departments.

The following code snippet creates a certificate signed by a previously issued root certificate. The serial number of the root certificate needs to be copied from the output of the previous section's script. Note that this time the first argument of the CreateCertificate method is set to the signer certificate's private key context obtained via the PrivateKeyContext property.

Set CM = Server.CreateObject("Persits.CryptoManager")

' Obtain the root certificate by serial number
CM.LogonUser "domain", "account", "password"

Set Store = CM.OpenStore( "ROOT", True )
Set RootCert = Store.Certificates("010FA974F6A20E8447A07F018AD34D56")

Set Context = CM.OpenContext("SubContainer", True )

' Subject is a CR/LF separated list of tagged values
Subject = "OU=Acme Software R&D CA" & chr(13) & chr(10)
Subject = Subject & "C=US" & chr(13) & chr(10)
Subject = Subject & "L=New York" & chr(13) & chr(10)
Subject = Subject & "S=NY" & chr(13) & chr(10)

' Create certificate valid for 365 days starting now
' The last argument specifies that the private key must be included when saved.
Set Cert = Context.CreateCertificate( RootCert.PrivateKeyContext, Subject, _
   Now(), Now() + 365, True)

' Save to HKEY_LOCAL_MACHINE-based ROOT store
Store.AddCertificate Cert

Response.Write "Certificate " & Cert.SerialNumber & " added."
ICryptoManager objCM = new CryptoManager();

// Obtain the root certificate by serial number
objCM.LogonUser( "domain", "account", "password", Missing.Value );
ICryptoStore objStore = objCM.OpenStore( "ROOT", true );
ICryptoCert objRootCert =
objStore.Certificates["010FA974F6A20E8447A07F018AD34D56"];

ICryptoContext objContext = objCM.OpenContext("SubContainer", true,Missing.Value );

//ubject is a CR/LF separated list of tagged values
String strSubject = "OU=Acme Software R&D CA\r\n";
strSubject += "C=US\r\n";
strSubject += "L=New York\r\n";
strSubject += "S=NY\r\n";

// Create certificate valid for 365 days starting now
// The last argument specifies that the private key must be included when saved.
ICryptoCert objCert = objContext.CreateCertificate(
objRootCert.PrivateKeyContext, strSubject,
   DateTime.Now, DateTime.Now.AddYears(1), true);

// Save to HKEY_LOCAL_MACHINE-based ROOT store
objStore.AddCertificate( objCert, Missing.Value );

txtResult.Text = "Certificate " + objCert.SerialNumber + " added.";

Before running this script, replace the arguments to the LogonUser method with a valid domain/username/password combination.

7.4 OpenContext and OpenContextEx Methods

Once your hierarchy of certificates is built you must distribute the root and intermediary certificates to client machines. To export a certificate to a file together with all the certificates in the certification path, you should use the CryptoCert method ExportToFilePKCS7, as follows:

Set CM = Server.CreateObject("Persits.CryptoManager")
CM.LogonUser "domain", "account", "password"

Set Store = CM.OpenStore("ROOT", True)
Set Cert = Store.Certificates("03804327900075BF11D3596C62FA5573")
Cert.ExportToFilePKCS7 "c:\path\acme.spc", True
ICryptoManager objCM = new CryptoManager();
objCM.LogonUser( "domain", "account", "password", Missing.Value );

ICryptoStore objStore = objCM.OpenStore("ROOT", true);
ICryptoCert objCert = objStore.Certificates["03804327900075BF11D3596C62FA5573"];
objCert.ExportToFilePKCS7( @"c:\path\acme.spc", true );

The last argument specifies that all certificates in the certification path should be included. A .spc file generated by this code can be installed on a client machine by simply clicking on the file from Windows Explorer. This will invoke the Certificate Manager Import wizard which will copy the certificates contained in the file to the current user's CA and ROOT stores.

7.5 Personal and SSL Certificate Live Demos

7.5.1 Personal Certificate Enrollment

For a user to obtain a personal certificate over the Web, the following steps must be taken:

  1. Generate a key pair on the client's machine.
  2. Create a Certificate Request (PKCS#10) from the client's public key and personal information such as company name, email address, locale, etc.
  3. Send the Certificate Request to the Certification Authority for verification.
  4. The CA verifies the information contained in the Certificate Request and generates a certificate signed with the CA's private key.
  5. The certificate is installed on the client's machine and connected to the appropriate key pair.

All steps except for Step 4 (certificate generation) take place on the client's machine. We would need a client-side ActiveX control to perform these tasks. Fortunately, such controls have been provided by Microsoft Windows.

On Windows NT, 2000 and XP, the name of the ActiveX control is XEnroll. It provides two useful methods: CreatePKCS10 which creates a key pair and generates the corresponding PKCS#10 Certificate Request, and AcceptPKCS7 which accepts the generated certificate, copies it to the current user's MY certificate store and connects it with the private key generated earlier.

On Windows Vista, 7 and onward, Microsoft has retired XEnroll and replaced it with CertEnroll, a new control with a more complex object model. CertEnroll refuses to run unless the page hosting it is executed under SSL or the browser is configured to allow ActiveX controls not marked safe for scripting.

To generate a certificate from a certificate request, AspEncrypt provides the CryptoContext method CreateCertificateFromRequest which accepts the signer's private key context, certificate request string and From/To validity dates as arguments.

Click on the following link to run our Personal CA Live Demo and download its source code:

7.5.2 Server SSL Certificate Enrollment

AspEncrypt can be used to issue SSL certificates as well. The server-side code is almost identical to the Personal CA version: the method CreateCertificateFromRequest is used again. The client-side code is actually much simpler since it does not require any ActiveX controls. The certificate request is generated by IIS Manager and the certificate is installed by it also.

Click on the following link to run our Server SSL CA Live Demo and download its source code:

Chapter 6: Digital Signatures Chapter 8: XEncrypt - Client-side ActiveX Control