10.4. CertificatesWhen you are given a public and private key, you often need to provide other people with your public key. If you sign a digital document (using your private key), the recipient of that document will need your public key in order to verify your digital signature. The inherent problem with a key is that it does not provide any information about the identity to which it belongs; a key is really just a sequence of seemingly arbitrary numbers. If I want you to accept a document that I digitally signed, I could mail you my public key, but you normally have no assurance that the key (and the original email) came from me at all. I could, of course, digitally sign the e-mail so that you knew that it came from me, but there's a circular chain here--without my public key, you cannot verify the digital signature. You would need my public key in order to authenticate the public key I've just sent you. Certificates solve this problem by having a well-known entity (called a certificate authority, or CA) verify the public key that is being sent to you. A certificate can give you the assurance that the public key in the certificate does indeed belong to the entity that the certificate authority says it does. However, the certificate only validates the public key it contains: just because Fred sends you his public key in a valid certificate does not mean that Fred is to be trusted; it only means that the public key in question does in fact belong to Fred. In practice, the key may not belong to Fred at all; certificate authorities have different levels at which they assess the identity of the entity named in the certificate. Some of these levels are very stringent and require the CA to do an extensive verification that Fred is who he says he is. Other levels are not stringent at all, and if Fred can produce a few dollars and a credit card, he is assumed to be Fred. Hence, one of the steps in the process of deciding whether or not to trust the entity named in the certificate includes the level at which the certificate authority generated the certificate. Each certificate authority varies in its approach to validating identities, and each publishes its approach to help you understand the potential risks involved in accepting such a certificate. A certificate contains three pieces of information (as shown in Figure 10-2):
Figure 10-2. Logical representation of a certificateBecause the certificate carries a digital signature of the certificate authority, we can verify that digital signature--and if the verification succeeds, we can be assured that the public key in the certificate does in fact belong to the entity the certificate claims (subject to the level at which the CA verified the subject). We still have a bootstrapping problem here--how do we obtain the public key of the certificate authority? We could have a certificate that contains the public key of the certificate authority, but who is going to authenticate that certificate? This bootstrapping problem is one reason why key management (see Chapter 11, "Key Management") is such a hard topic. Most Java-enabled browsers solve this problem by providing the public keys for certain well-known certificate authorities along with the browser. This has worked well in practice, though it clearly is not an airtight solution (especially when the browser is downloaded from some site on the Internet--theoretically, the certificates that come with the browser could be tampered with as they are in transit). Although there are various proposals to strengthen this model, for now we will assume that the certificate of at least one well-known certificate authority is delivered along with the Java application. This situation allows me to mail you a certificate containing my public key; if the certificate is signed by a certificate authority you know about, you are assured that the public key actually belongs to me. There are many well-known certificate authorities--and therein lies another problem. I may send you a certificate that is signed by the United States Post Office, but that certificate authority may not be one of the certificate authorities you recognize. Simply sending a public key in a certificate does not mean that the recipient of the public key will accept it. A more important implication of this is that a key management system needs to be prepared to assign multiple certificates to a particular individual, potentially one from each of several certificate authorities. Another implication of this profusion of certificate authorities is that certificates are often supplied as a chain. Let's say that you have the certificate of the U.S. Post Office certificate authority, and I want to send you my certificate that has been generated by the Acme Certificate company. In order for you to accept this certificate, I must send you a chain of certificates: my certificate (certified by the Acme Certificate company), and a certificate for the Acme Certificate company (certified by the U.S. Post Office). This chain of certificates may be arbitrarily long. The last certificate in this chain--that is, the public key for a certificate authority--is generally stored in a certificate that is self-signed: the certificate authority has signed the certificate that contains its own public key. Self-signed certificates tend to crop up frequently in the Java world as well, since the tools that come with the JDK will create self-signed certificates. The certificates are intended to be submitted to a certificate authority, who will then return a CA-signed certificate. But there's no reason why the certificate itself can't be used as a valid certificate. Whether or not you want to accept a self-signed certificate is up to you, but it obviously carries certain risks. Finally, for all this talk of certificates, you have to consider whether or not they are actually necessary to support your application. If you'll generally be receiving signed items from people you do not know (e.g., a signed JAR file from a web site), then they are absolutely necessary. On the other hand, large-scale computer installations often consider using certificates to authenticate and validate their employees; this results in a computer system that has much better internal security than one that relies solely on passwords. But it is not the certificate that generates the security advantage, it is the use of public key cryptography. The computer installation can achieve the same level of security without using a certificate infrastructure. Consider the security necessary to support XYZ Corporation's payroll application. When an employee wants to view her payroll statements, she must submit a digitally signed request to do so. Hence, XYZ should distribute to each employee a private key to be used to create the digital signature. XYZ can also store the employee's public keys in a database; when a request comes that claims to be from a particular employee, the payroll server can simply examine the database to obtain that employee's public key and verify the signature. No certificate is required in this case--and in general, no certificate is required when the recipient of the digital signature is already known to have the public key of the entity that signed the data. For applications within a corporation, this is almost always the case. We issue this caveat about certificates being necessary because certificate support in Java (even in Java 1.2) is not fully complete--while it is possible to set up your own certificate authority to distribute the certificates for your company, it's very hard to write the necessary code to do that in Java (at present). Hence, we'll focus our discussion of the certificate API on accepting (i.e., validating) existing certificates. 10.4.1. The Certificate ClassThere are many formats that a certificate can take (depending on the cryptographic algorithms used to produce the certificate). Hence, the Java API abstracts the generic notion of a certificate with the Certificate class (java.security.cert.Certificate):
Like many classes in the Java security package, the Certificate class is abstract; it relies upon application-specific classes to provide its implementation. In the case of the JDK, there are classes in the sun package that implement certain certificate formats (but more about that in just a bit). There are three essential operations that you can perform upon a certificate:
These are the basic operations that are valid for any certificate. Notice that while we can encode a certificate into a byte array in order to transmit the certificate, there is nothing in the basic API that allows us to create a certificate from such a byte array. In fact, there's no practical way to instantiate a certificate object at all; the Certificate class is usually used as a base class from which individual certificate types are derived. Fortunately, the next class allows us to import certificates. 10.4.2. The CertificateFactory ClassIf you need to import a certificate into a program, you do so by using the CertificateFactory class (java.security.cert.CertificateFactory). That class is an engine class, and it has the following interface:
Note that the CertificateFactory class cannot generate a new certificate--it may only import a certificate from an input stream. This is one reason why it's hard to provide a certificate authority based solely on the standard Java API. In the next section, we'll see an example of reading a certificate through this interface. The CertificateFactory is an engine class, so it has a companion SPI class--the CertificateFactorySpi class--that can be used if you want to implement your own certificate factory. Implementing such a class follows the familiar rules of engine classes: you must define a constructor that takes the type name as a parameter and then, for each of the public methods listed above, you must implement a corresponding engine method with the same parameters. Certificates are complicated things, and parsing their encoding is a complicated procedure, so we won't bother showing an example of the engine class. 10.4.3. The X509Certificate ClassAs we mentioned, there are many certificate formats that could be in use by a key management system; one of the most common of these is the X509 format. X509 has gone through a few revisions; the version supported by the Java API is version 3. This format is an ANSI standard for certificates, and while there are PGP and other certificate formats in the world, the X509 format is dominant. This is the only format of certificate for which Java provides a standard API; if you want to support another certificate format, you must implement your own subclass of Certificate. The X509Certificate class (java.security.cert.X509Certificate) is defined as follows:
An X509 certificate has a number of properties that are not shared by its base class:
These properties can be retrieved with the following set of methods:
From a programmatic view, these are the most useful of the attributes of a certificate. If your X509 certificate is contained in the file sdo.cer, you could import and print out information about the certificate as follows: Class Definitionpublic class PrintCert { public static void main(String args[]) { try { FileInputStream fr = new FileInputStream("sdo.cer"); CertificateFactory cf = CertificateFactory.getInstance("X509"); X509Certificate c = (X509Certificate) cf.generateCertificate(fr); System.out.println("Read in the following certificate:"); System.out.println("\tCertificate for: " + c.getSubjectDN()); System.out.println("\tCertificate issued by: " + c.getIssuerDN()); System.out.println("\tThe certificate is valid from " + c.getNotBefore() + " to " + c.getNotAfter()); System.out.println("\tCertificate SN# " + c.getSerialNumber()); System.out.println("\tGenerated with " + c.getSigAlgName()); } catch (Exception e) { e.printStackTrace(); } } } Running this program would produce the following output: Class DefinitionRead in the following certificate: Certificate for: CN=Scott Oaks, OU=SMCC, O=Sun Microsystems, L=NY, S=NY, C=US Certificate issued by: CN=Scott Oaks, OU=SMCC, O=Sun Microsystems, L=NY, S=NY, C=US The certificate is valid from Sun Oct 19 11:40:24 EDT 1997 to Sat Jan 17 10:40:24 EST 1998 Certificate SN# 3895020084 Generated with SHA1withDSA 10.4.4. Advanced X509Certificate MethodsThere are a number of other methods of the X509Certificate class. For the purposes of this book, these methods are not generally useful; they enable you to perform more introspection on the certificate itself. We'll list these methods here simply as a matter of record.
10.4.5. Revoked CertificatesOccasionally, a certificate authority needs to revoke a certificate it has issued--perhaps the certificate was issued under false pretenses, or maybe the user of the certificate has engaged in illegal conduct using the certificate. Under circumstances such as these, the expiration date attached to the certificate is insufficient protection; the certificate must be immediately invalidated. This invalidation occurs as the result of a CRL--a certificate revocation list. Certificate authorities are responsible for issuing certificate revocation lists that contain (predictably) a list of certificates the authority has revoked. Validators of certificates are required to consult this list before accepting the validity of a certificate. Unfortunately, the means by which an authority issues a CRL is one of those areas that is in flux, and while the interfaces to support revoked certificates have been established, they are not completely integrated into most certificate systems. In particular, the validate() method of the Certificate class does not automatically consult any CRL. The CRL itself is typically obtained in an out-of-band fashion (just as the certificates of the authority were obtained); once you have a CRL, you can check to see if a particular certificate in which you are interested is on the list. While the notion of revoked certificates in not necessarily specific to an X509 certificate, the Java implementation is. Revoked certificates themselves are represented by the X509CRLEntry class (java.security.cert.X509CRLEntry):
The methods of this class are simple and are based upon the fields present in a revoked X509 certificate:
Revoked certificates are modeled by the X509CRL class (java.security.cert.X509CRL):
Instances of the X509CRLEntry class are obtained by the getInstance() method of the CertificateFactory. Once the class has been instantiated, you may operate upon it with these methods. As you can see, there is a strong synergy between the methods that are used to operate upon an X509 certificate and those used to operate upon a CRL:
There is one more method of the X509CRL class, which it inherits from its superclass, the CRL class (java.security.cert.CRL):
When all is said and done, the point of the CRL class (and the revoked certificate class) is to provide you with the tools necessary to see if a particular certificate has been invalidated. This checking is up to your application to perform; you might choose to implement it as follows: Class Definitionpublic Certificate importCertificate(byte data[]) throws CertificateException { X509Certificate c = null; try { CertificateFactory cf = CertificateFactory.getInstance("X509"); ByteArrayInputStream bais = new ByteArrayInputStream(data); c = (X509Certificate) cf.generateCertificate(bais); Principal p = c.getIssuerDN(); PublicKey pk = getPublicKey(p); c.verify(pk); InputStream crlFile = lookupCRLFile(p); cf = CertificateFactory.getInstance("X509CRL"); X509CRL crl = (X509CRL) cf.generateCRL(crlFile); if (crl.isRevoked(c)) throw new CertificateException("Certificate revoked"); } catch (NoSuchAlgorithmException nsae) { throw new CertificateException("Can't verify certificate"); } catch (NoSuchProviderException nspe) { throw new CertificateException("Can't verify certificate"); } catch (SignatureException se) { throw new CertificateException("Can't verify certificate"); } catch (InvalidKeyException ike) { throw new CertificateException("Can't verify certificate"); } catch (CRLException ce) { // treat as no crl } return c; } This method encapsulates importing a certificate and checking its validity. It is passed the DER-encoded data of the certificate to check (this data must have been read from a file or other input stream, as we showed earlier). Then we consult the certificate to find out who issued it, obtain the public key of the issuer, and validate the certificate. Before we return, however, we obtain the latest CRL of the issuing authority and ensure that the certificate we're checking has not been revoked; if it has been, we throw a CertificateException. We've glossed over two details in this method: how we obtain the public key of the authority that issued the certificate, and how we get the CRL list associated with that authority. Implementing these methods is the crux of a key/certificate management system, and we'll show some ideas on how to implement the key lookup in Chapter 11, "Key Management". Obtaining the CRL is slightly more problematic, since you must have access to a source for the CRL data. Once you have that data, however, it's trivial to create the CRL via the generateCRL() method. Copyright © 2001 O'Reilly & Associates. All rights reserved. |
|