Construct secure networked applications with certificates, Part 3

Use the Java CRL and X509CRL classes

In the last two columns I laid the foundation for and introduced the key players in the emerging public-key infrastructure (PKI) space. You have already met the X.509 certificate and know how to use it in your Java applications. This month, I will introduce its lesser-known sibling -- the X.509 certificate revocation list (CRL).

You can read the whole series on certificates:

Your public key and private key provide a mechanism by which you can establish, and others can verify, your online identity -- an important ingredient for trusted transactions. Your public key, properly vouched for by a certificate authority (CA), offers a third party with a reliable means to check your identity -- provided your private key remains private.

Unfortunately, public-key cryptography is effective only if your private key is secret. If a hostile entity obtains a copy of your private key, that entity can impersonate you electronically. Less critically, if you lose your private key, you lose your ability to electronically prove your identity.

For those reasons, an infrastructure built on public-key cryptography, certificates, and CAs must include the functionality of certificate revocation as well as certificate issuance. The issuance of a certificate states that the CA believes, to the best of its ability, that the entity named in the issued certificate is associated with the public key enclosed in the issued certificate. The revocation of a certificate indicates that the CA is no longer willing to make that claim because of the issues raised above or any number of other concerns.

A CA formally revokes a previously issued certificate by including information that identifies it -- its serial number -- in a periodically published CRL.

CRL

A CRL is literally a digitally signed list that contains the serial numbers and associated data (such as the revocation date) of public-key certificates that were issued and subsequently revoked by the CA. When published by a CA, a CRL is placed in a public repository or otherwise made widely available so that applications can check the revocation status of certificates before accepting them.

RFC 2459, published by the Public Key Infrastructure X.509 (PKIX) working group of the Internet Engineering Task Force (IETF), defines the format of X.509 CRLs. Like X.509 certificates, the format of an X.509 CRL is specified in ASN.1 (Abstract Syntax Notation One) notation. The ASN.1 Distinguished Encoding Rules (DER) defines a platform-independent binary format.

Just as I did last month, I will avoid going into the depths of ASN.1 notation and DER encoding and instead concentrate on Sun's CRL API.

The CRL API

As it does with certificates, the Java CRL API provides both a generic and an X.509-specific interface to CRL functionality.

The java.security.cert.CRL class defines the generic interface. The java.security.cert.X509CRL class, a subtype of the generic java.security.cert.CRL class, defines the X.509-specific interface.

A final class, the java.security.cert.X509CRLEntry class, provides access to each entry in an X.509 CRL.

Let's look at each class in turn.

java.security.cert.CRL

Class java.security.cert.CRL defines the interface common to all types of CRLs. I will describe this class's most important method:

  • public boolean isRevoked(Certificate certificate) indicates whether or not the specified certificate is revoked by checking for inclusion in the revocation list contained in this instance.

java.security.cert.X509CRL

The class java.security.cert.X509CRL extends the CRL class described above and adds X.509-specific functionality:

  • public byte [] getEncoded() returns the encoded form of the CRL represented by the instance upon which this method is invoked.
  • public Date getNextUpdate() returns the date by which the CA will release an updated CRL. The next CRL could be issued earlier than that date, but it will not be issued later.
  • public Date getThisUpdate() returns the date on which the CA released this CRL.

The X509CRL class also contains methods for enumerating the entries that make up the certificate revocation list. I'll talk about the X509CRLEntry class below:

  • public Set getRevokedCertificates() returns the set of entries representing revoked certificates.
  • public X509CRLEntry getRevokedCertificate(BigInteger serialnumber) returns an entry representing the revoked certificate with the specified serial number.

Most of the remaining methods are query methods that return information about the CRL itself (as opposed to its entries):

  • public int getVersion() returns the CRL's version.
  • public abstract Principal getIssuerDN() returns information that identifies the CRL's issuer, which is the entity that signed the CRL.
  • public String getSigAlgName(), public String getSigAlgOID(), and public byte [] getSigAlgParams() return information about the algorithm (and its parameters) used to sign the CRL.
  • public byte [] getSignature() returns the raw signature information for the CRL.
  • public byte [] getTBSCertList() returns the raw TBS (to be signed) information for this CRL. Most likely, you will not use this method; it exists primarily so that you can independently verify the CRL's signature (outside of the API).

Finally, the X509CRL class contains methods for establishing the validity of the X509CRL instance:

  • public void verify(PublicKey publickey), as its name suggests, verifies that the CRL instance was signed with the private key corresponding to the specified public key.
  • public void verify(PublicKey publickey, String stringProvider) performs the same operation as above using the specified service provider, rather than the default.

java.security.cert.X509CRLEntry

The java.security.cert.X509CRL class contains two methods -- getRevokedCertificate() and getRevokedCertificates() -- that return one or more entries in the revocation list as instances of the java.security.cert.X509CRLEntry class. The following methods provide information about these entries:

  • public byte [] getEncoded() returns the encoded form of the entry represented by the instance upon which this method is invoked.
  • public BigInteger getSerialNumber() returns the revoked certificate's serial number.
  • public Date getRevocationDate() returns the certificate's revocation date.
  • public boolean hasExtensions() indicates whether or not the CRL entry has extensions. Unfortunately, the API doesn't provide access to these extensions as it does for X.509 certificate extensions. If you want them, you must parse the encoded form of the entry obtained from the getEncoded() method.

The code

The following class demonstrates how to obtain a certificate factory, how to use the factory to generate a X.509 CRL from the DER-encoded representation in a file, and how to extract and display information about the CRL.

import java.io.FileInputStream;
import java.util.Set;
import java.util.Iterator;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLEntry;
public
class Main
{
  public
  static
  void
  main(String [] arstring)
  {
    try
    {
      // Get the correct certificate factory.
      CertificateFactory certificatefactory =
        CertificateFactory.getInstance("X.509");
      // Each file specified on the command line must contain a single
      // DER-encoded X.509 CRL.
      for (int i = 0; i < arstring.length; i++)
      {
        // Open the file.
        FileInputStream fileinputstream = new FileInputStream(arstring[i]);
        // Generate a certificate from the data in the file.
        X509CRL x509crl =
          (X509CRL)certificatefactory.generateCRL(fileinputstream);
        // Print out information about the crl.
        System.out.println("---CRL---");
        System.out.println("type = " +
          x509crl.getType());
        System.out.println("version = " +
          x509crl.getVersion());
        System.out.println("issuer = " +
          x509crl.getIssuerDN().getName());
        System.out.println("signing algorithm = " +
          x509crl.getSigAlgName());
        System.out.println("signing OID = " +
          x509crl.getSigAlgOID());
        System.out.println("this update = " +
          x509crl.getThisUpdate());
        System.out.println("next update = " +
          x509crl.getNextUpdate());
        System.out.println();
        // Next, let's print out information about the entries.
        System.out.println("---Entries---");
        Set setEntries = x509crl.getRevokedCertificates();
        if (setEntries != null && setEntries.isEmpty() == false)
        {
          for (Iterator iterator = setEntries.iterator();
               iterator.hasNext(); )
          {
            X509CRLEntry x509crlentry = (X509CRLEntry)iterator.next();
            System.out.println("serial number = " +
              x509crlentry.getSerialNumber());
            System.out.println("revocation date = " +
              x509crlentry.getRevocationDate());
            System.out.println("extensions = " +
              x509crlentry.hasExtensions());
            System.out.println();
          }
        }
        // We're done.
        System.out.println("---");
        // Close the file.
        fileinputstream.close();
      }
    }
    catch (Exception exception)
    {
      exception.printStackTrace();
    }
  }
}

You should be able to compile the class definition in the listing above into a classfile. When you execute the class, you should specify the names of one or more CRL files on the command line. I've included a sample CRL in the downloadable source code in Resources.

Alternatives

CRLs, while obviously necessary and actually fine for many applications, suffer from two drawbacks.

The first deals with update latency. CRL distribution is a push scheme -- meaning the CA pushes new CRLs to the public distribution point(s) according to some timetable. In order for CRLs to be effective, the CA must update them regularly and often. But no matter how often the update occurs, there will always be some application that will need more frequent updates. A CRL that publishes every hour may publish too frequently for certificates used for applet code signing, but not frequently enough for certificates that secure financial and banking transactions.

The second drawback deals with the length of the CRL. Certificates are issued for a fixed period of time -- for example, 180 days. If a certificate must be revoked before its expiration date, you can do so by adding it to a CRL. The entry must remain in the CRL until the certificate expires, and if there are many revocations, a CRL can grow very large. Unfortunately, the average length of a CRL will grow as the popularity of PKI in general grows -- not a good approach to scalability.

Conclusion

You now know how to use certificates, which are useful for establishing your identity online, and CRLs, which are useful when the private key that protects your certificate falls into the wrong hands. Now that you understand both certificates and CRLs, and the Java APIs that manipulate them, you're ready to put them to use. In Part 4 of this series, you'll look at practical applications for the certificates in your projects.

Todd Sundsted has been writing programs since computers became available in desktop models. Though originally interested in building distributed applications in C++, Todd moved on to the Java programming language when it became the obvious choice for that sort of thing. In addition to writing, Todd is cofounder and chief architect of PointFire.

Learn more about this topic