Chapter 4: Certificates and Certificate Stores

Contents

4.1 OpenContext and OpenContextEx Methods

4.1.1 Public-key Cryptography

Symmetric encryption is great but it requires that the two parties engaging in secure communications should agree on a cipher key in advance before they can start exchanging messages securely.

Public-key cryptography has changed that. Brilliant scientific minds have invented ciphers that involve two keys instead of one: private and public. The two are mathematically related but, for all practical purposes, cannot be deduced one from the other. A message encrypted with the public key can only be decrypted with the corresponding private key, and vice versa. By far the most popular public-key cipher is RSA.

Now, if Alice wants to send a secure message to Bob, all she needs to do is take Bob's public key and encrypt the message with it. No one except Bob can decrypt such a message since only Bob has the corresponding private key. No prior arrangement between Alice and Bob is necessary.

The process can work in reverse as well: if Bob encrypts a document with his private key and sends it to Alice, she can decrypt the document with Bob's public key thus verifying that it did indeed come from Bob and not anyone else, and was not modified during transmission. Thus encryption with one's private key creates a digital signature.

In real life, a message is never encrypted directly with a public or private key for performance reasons -- public-key ciphers are very slow. The message is encrypted with a random symmetric key and it is that key that is encrypted with the public key and sent along with the encrypted message. Likewise, a digital signature is computed from the document's hash value, not from the entire document.

But how can Alice know that the public key she has obtained really belongs to Bob and not someone else? The short answer is: she can't. This is where certificates come in handy.

4.1.2 What's a Certificate?

A certificate is a data package that contains the entity's details (such as its name, email address, organization, etc.) bundled with this entity's public key and is issued (and digitally signed) by an independent trusted third party called a Certification Authority (CA) only after that authority has verified its identity. An entity can be a private individual or organization. A certificate is essentially a vehicle for transporting public keys in a uniform and verifiable fashion.

We deal with digital certificates on a regular basis without even noticing it. Every time we purchase something online with a credit card, the information we submit is protected by Secure Socket Layer (SSL), a protocol based on public-key cryptography. The public key used by your browser to communicate securely with the web server is provided by the server via an SSL certificate. Its details can be viewed by clicking on the little lock icon displayed by the browser every time it operates under SSL.

4.1.3 Get Your Own Certificate!

To be able to experiment with the code samples in this and following chapters, you need a personal certificate of your own. You can obtain one from any certification authority you like. We recommend getting a free secure email certificate from InstantSSL by COMODO.

Another option is Verisign but their personal certificates are not free. You can also obtain a free demo certificate from AspEncrypt's CA Live Demo application here.

To view the certificates stored on your machine, open Internet Explorer, go to Tools/Internet Options, open the Content tab and click on the Certificates button.

Note the text at the bottom of the Certificate dialog says "You have a private key that corresponds to this certificate." Only your personal certificates have a private key associated with them, all other certificates you will find on your machine do not.

4.2 Working with Certificate Stores

Certificates on your machine are kept in certificate stores in the system registry. There are four stores of interest: personal (MY), other people (AddressBook), intermediate CA (CA) and root CA (ROOT). The certificate stores are created for each interactive user as well as the local machine as a whole.

AspEncrypt represents a certificate store via the CryptoStore object. An instance of this object is created via CryptoManager's OpenStore method, as follows:

Set Store = CM.OpenStore( "MY", True )

CryptoStore objStore = objCM.OpenStore( "MY", true );

The first argument to OpenStore is the name of the store. The valid values are "MY" for the personal store, "AddressBook" for the other people store, "CA" for the intermediary CA store, and "ROOT" for the Root CA store. The 2nd argument specifies whether the store to be opened belongs to the current user (if set to False) or the local machine (if set to True.)

In an ASP/ASP.NET environment, if anonymous access is enabled, an attempt to open a store will probably result in an Access is denied error. To avoid this error, impersonation of an admin account should be used with the method LogonUser, as follows:

CM.LogonUser "domain", "account", "password" Set Store = CM.OpenStore( "MY", True )
objCM.LogonUser( "domain", "account", "password", Missing.Value ); ICryptoStore objStore = objCM.OpenStore( "MY", true );

To obtain a list of all certificates residing in a store, the Certificates property of the CryptoStore object should be used. This property returns an instance of CryptoCerts, a collection of CryptoCert objects each representing a certificate in this store. CryptoCert's various properties return the certificate's attributes such as its subject, issuer, issue and expiration dates and other information. The following code snippet displays the subject and issuer information of all certificates residing in the MY store of the current user (the one being impersonated by the LogonUser method:)

Set CM = Server.CreateObject("Persits.CryptoManager") CM.LogonUser "domain", "account", "xxxxxx" Set Store = CM.OpenStore( "MY", false ) For Each Cert in Store.Certificates Response.Write Cert.Subject & "---" & Cert.Issuer & "<BR>" Next
ICryptoManager objCM = new CryptoManager(); objCM.LogonUser( "domain", "account", "xxxxxx", Missing.Value ); ICryptoStore objStore = objCM.OpenStore( "MY", false ); foreach( ICryptoCert objCert in objStore.Certificates ) { txtResult.Text += objCert.Subject[Missing.Value] + "---" + objCert.Issuer[Missing.Value]; }

The CryptoCert properties Subject and Issuer deserve special attention. These properties return CryptoName objects containing detailed information about the entity to whom the certificate is issued, and the issuing authority, respectively. The CryptoName object has a default Item property which accepts an optional string index argument. For our personal certificate shown above, the expression Cert.Issuer (VB Script) or Cert.Issuer[Missing.Value] (C#) returns the following CR/LF-separated string:

E=info@persits.com
C=US
S=NY
L=New York
CN=Persits Software Demo CA New York

The expression Cert.Subject returns the string

E=peter@persits.com
CN=Peter

You can see that a certificate's Subject and Issuer properties consist of several tagged components separated by a CR/LF sequence. The most common ones are CN (common name), E (email), O (organization), OU (organizational unit), L (locale), S (state), and C (country).

The CryptoName object allows you to obtain the individual components of a name by specifying the appropriate tag as Item's argument. For example, the expression Cert.Issuer("CN") (VB Script) or Cert.Issuer["CN"] (C#) returns the string

Persits Software Demo CA New York

The CryptoName object also provides the property Name, which looks for the components CN, OU, O, and E in this order and returns the first non-empty value found.

The CryptoStore.Certificates collection allows you to obtain a particular certificate by 1-based index, and also by serial number. A certificate's serial number, as well as all other properties can be viewed on the Details tab of the Certificate dialog in IE:

The serial number can be copied from this dialog box and pasted directly into your script as the index argument to Store.Certificates(...), as follows:

Set Cert = Store.Certificates("d5 b9 c8 38 8f fb 41 b0 43 d6 47 2b b9 58 44 5e")

ICryptoCert objCert = objStore.Certificates["d5 b9 c8 38 8f fb 41 b0 43 d6 47 2b b9 58 44 5e"];

The spaces in the index value are ignored, so the expression

Set Cert = Store.Certificates("d5b9c8388ffb41b043d6472bb958445e")

is also valid.

In addition to the Subject and Issuer properties covered above, the CryptoCert object also offers properties corresponding to the certificate property dialog shown above, and several others unrelated to that dialog. These properties are:

BasicConstraints
IssuerCert
KeyUsage
NotAfter
NotBefore
SerialNumber
Sha1Hash
SignatureAlgID
SignatureAlgorithm
StoreName
PrivateKeyContext
PrivateKeyExists
PublicKey
PublicKeyLength
Version

For details, see the CryptoCert object reference.

The following code sample allows you to browse all current-user and local-machine certificates on your machine and view their properties. Before running this code sample, you must replace "generic" arguments to the LogonUser method with your own valid domain, username and password in the files 04_certlist.asp/aspx and 04_cert.asp/aspx.

Click the links below to run this code sample:

4.3 Certificate Exporting and Importing

A certificate can be exported from its certificate store to a disk file, either manually or programmatically with the help of AspEncrypt. Once a certificate has been exported to a file, it can be moved to other machines. AspEncrypt is capable of opening a certificate from a file as easily as from a certificate store.

To export a certificate manually, open the certificate properties in IE and click on the Export... button. If the certificate you are exporting has a private key associated with it, you will be asked if you wish to export the private key along with it. Say "No" to export the public-key portion of the certificate only. You will then be presented with several certificate format options:

The first two options are virtually identical for all practical purposes and create a certificate file with the extension ".cer". The first option specifies binary encoding and the second Base64 encoding. AspEncrypt recognizes both formats. To load a .cer file into a CryptoCert object, the CryptoContext method ImportCertFromFile should be used, as follows:

Set Cert = CM.ImportCertFromFile( "c:\path\mycert.cer" )
ICryptoCert objCert = objCM.ImportCertFromFile( @"c:\path\mycert.cer" );

The third option, Cryptographic Message Syntax Standard, creates a file that is essentially a certificate store. It may contain not only the certificate being exported, but also the certificates in its certification path as well (if the corresponding box is checked.) The file extension of such file-based certificate stores are usually either ".p7b" or ".spc".

To load such a file into a CryptoStore object, use the CryptoContext method ImportStoreFromFile and then obtain a specific certificate (as there can be several) from the Certificates collection via a 1-based index or serial number:

Set Store = CM.ImportStoreFromFile( "c:\path\mystore.p7b" )
Set Cert = Store.Certificates(1)
ICryptoStore objStore = objCM.ImportStoreFromFile( @"c:\path\mystore.p7b" );
ICryptoCert objCert = objStore.Certificates[1];

In addition to ImportCertFromFile and ImportStoreFromFile, AspEncrypt also offers two methods that import the certificates and stores from binary arrays as opposed to disk files. These methods are ImportCertFromBlob and ImportStoreFromBlob. These methods are identical to their "file" counterparts but they accept a CryptoBlob object as the argument instead of a file path. These methods are useful if your certificates are stored in the database as blobs. For example:

Set Blob = CM.CreateBlob
Blob.Binary = rs("cert_field").Value
Set Cert = CM.ImportCertFromBlob( Blob )
ICryptoBlob objBlob = objCM.CreateBlob();
objBlob.Binary = rs["cert_field"].Value;
ICryptoCert objCert = objCM.ImportCertFromBlob( objBlob );

Note that a certificate loaded from a .cer or .p7b file can only be used for data encryption, not signing, because it does not have a private key associated with it.

4.4 Support for PKCS#12 (PFX) Format

During the certificate exporting procedure described in the previous section, if you select "Yes" when asked if you want to export the private key, you will then be asked to specify and confirm a password and the certificate will be saved in the Personal Information Exchange (PFX) format also known as PKCS#12. PFX files store certificates together with their private keys and usually have the extension ".pfx" or ".p12". Private key information in such files is protected with a password.

To retrieve a certificate from a PFX file with AspEncrypt, use the CryptoContext method OpenStoreFromPFX which returns a CryptoStore object representing all certificates contained in the PFX file, as follows:

Set Store = CM.OpenStoreFromPFX( "c:\path\file.pfx" )
Set Cert = Store.Certificates(1)
ICryptoStore objStore = objCM.OpenStoreFromPFX( @"c:\path\file.pfx" );
ICryptoCert objCert = objStore.Certificates[1];

Prior to Version 2.10.0.2, for this method to work, the method CM.LogonUser impersonating an admin account had to be called prior to calling OpenStoreFromPFX. Otherwise the errors

The system cannot find the file specified.

or

An internal error occurred.

would occur. With AspEncrypt 2.10.0.2+, you no longer need to call LogonUser.

Unlike a certificate loaded from a .cer or .p7b file, a PFX-based certificate can be used for both encryption and signing since the private key associated with it is available.

4.5 Transferring Client Certificates to the Server

In many applications, it is necessary for the public-key portion of a user's personal certificate to be transferred to, and stored at, the web server. For example, if a user wishes to sign up to periodically receive encrypted email from an online resource (such as his bank's or insurance company's web site) he needs to obtain a personal certificate from a CA of his choice, and send the public-key portion of the certificate (in other words, his .cer file) to the web site's administrator for storage. This .cer file is then used by the secure-mail application on that web site to encrypt the email sent to that user (AspEncrypt's secure email functionality is described in the next chapter.)

The user can be instructed to export the certificate to a file and then upload this file to the web server via a web form, or email it in as an attachment. But there is an easier way: to configure a virtual directory to require client certificates (you will need an SSL server certificate for this to work):

When trying to access such a resource, the user will be prompted by the browser to select one of his client certificates:

A certificate selected this way will be uploaded to the server automatically and become available to server-side scripts via the Request.ClientCertificate collection. To capture it, use the following code (note that the ASP and ASP.NET scripts are not entirely identical in the way they use the ClientCertificate collection):

Set CM = Server.CreateObject("Persits.CryptoManager")
Set Blob = CM.CreateBlob
Blob.Binary = Request.ClientCertificate("Certificate").Item

Set Cert = CM.ImportCertFromBlob( Blob )

Response.Write Cert.Subject & "----" & Cert.Issuer

' Save to the server's hard drive
Blob.DumpToFile "c:\certs\name.cer"
ICryptoManager objCM = new CryptoManager();
ICryptoBlob objBlob = objCM.CreateBlob();
objBlob.Binary = Request.ClientCertificate.Certificate;

ICryptoCert objCert = objCM.ImportCertFromBlob( objBlob );
Response.Write( objCert.Subject[Missing.Value] + "---" +
   objCert.Issuer[Missing.Value] );

// Save to the server's hard drive
objBlob.DumpToFile( @"c:\certs\name.cer" );

Note: Not all client certificates can be used this way. For example, the InstantSSL certificates do not appear in the Confirm-Certificate dialog shown by the browser as their enhanced key usage attribute disallows them from being used for client authentication purposes. However, the demo certificates obtained from our live demo do appear in the Confirm-Certificate dialog.

4.6 Moving Certificates from HKEY_CURRENT_USER to HKEY_LOCAL_MACHINE

AspEncrypt is based on Microsoft CryptoAPI. Under certain conditions, some CryptoAPI functions display warning messages, such as this one:

The conditions under which the CryptoAPI displays warning messages include, but are not limited to, the following:

  • An attempt to add a certificate to, or remove a certificate from, the HKEY_CURRENT_USER-based ROOT store.
  • An attempt to generate a signature with a private key created with the security flag set to USER_PROTECTED and stored in an HKEY_CURRENT_USER-based key container.
  • An attempt to decrypt an encrypted message with a private key created with the security flag set to USER_PROTECTED and stored in an HKEY_CURRENT_USER-based key container..

Performing these tasks with AspEncrypt in an ASP/ASP.NET environment may result in hanging up your web server. However, this is not the case for stores and private keys located in the HKEY_LOCAL_MACHINE section of the registry.

Therefore, when using AspEncrypt in an ASP/ASP.NET environment to perform private key-related operations such as generating signed mail messages, you should move the personal certificates you want to use from the HKEY_CURRENT_USER to HKEY_LOCAL_MACHINE section of the registry. This way you make these certificates readily available to your Web application and remove the USER_PROTECTED flag from the corresponding private keys to avoid the "underwater rock" problem just described.

To facilitate the moving procedure, AspEncrypt provides the CryptoCert method TransferToLocalMachine which moves the certificate to the specified HKEY_LOCAL_MACHINE store together with its private key.

The following client-side script moves a certificate to the HKEY_LOCAL_MACHINE-based MY store:

Set Store = CM.OpenStore("MY", False) ' Open MY store at HKCU
Set Cert = Store.Certificates("d5b9c8388ffb41b043d6472bb958445e")
Cert.TransferToLocalMachine "MY" ' Transfer to MY store at HKLM
ICryptoStore objStore = objCM.OpenStore("MY", false); // Open MY store at HKCU
ICryptoCert objCert = objStore.Certificates["d5b9c8388ffb41b043d6472bb958445e"];
objCert.TransferToLocalMachine( "MY" ); // Transfer to MY store at HKLM

The AspEncrypt installation includes CertMover.exe, a utility application based on the TransferToLocalMachine method. You can use this utility to transfer certificates from the HKCU to HKLM sections of the registry. This application can be found in the directory \Samples\manual_04 of the installation.

For a private key to be movable (or otherwise exportable), it needs to have been created with the CRYPT_EXPORTABLE flag set. Otherwise, when attempting to use the CertMover application, you will receive a Bad Key or Key not valid for use in specified state error message. When enrolling for a personal certificate, make sure the Exportable option is checked, provided the certification authority of your choice gives you this option. The InstantSSL by COMODO CA mentioned above conveniently makes its certificates exportable by default.

Chapter 3: One-way Hash Function Chapter 5: Secure Email