X.509 Certificate validation with Java and Bouncycastle

Rob picture Rob · Mar 16, 2010 · Viewed 27.7k times · Source

through the bouncycastle wiki page I was able to understand how to create a X.509 root certificate and a certification request, but I do not quite understand how to proceed concept- and programming wise after that.

Lets assume party A does a cert request and gets his client certificate from the CA. How can some party B validate A's certificate? What kind of certificate does A need? A root certificate? A 'normal' client certificate?

And how does the validation work on programming level, if we assume that A has successfully send his certificate in DER or PEM format to B?

Any help is much appreciated.

Best Regards, Rob

Answer

erickson picture erickson · Mar 16, 2010

From a programmer's perspective, you need a few things to validate an X.509 certificate.

  1. A set of "trust anchors"—the root certificates of CAs that you rely on. These should be protected from tampering, so that an attacker doesn't replace a CA certificate with his own fake. The public keys in these certificates are used to verify the digital signatures on other certificates.
  2. A collection of Intermediate certificates. The application might keep a collection of these, but most protocols, like SSL and S/MIME, that use certificates have a standard way to provide extra certificates. Storing these doesn't require any special care; their integrity is protected by the signature of a root CA.
  3. Revocation information. Even if a certificate was issued by a CA, it might have been revoked prematurely because the private key was disclosed, or the end entity changed their identity. (For example, a person switches jobs and a certificate with their old company's name in it is revoked.) CRLs or a web-service like OCSP can be used to get an update about the status of a certificate.

With these inputs available, you can use the built-in PKIX support to construct and validate a certificate path.

/* Givens. */
InputStream trustStoreInput = ...
char[] password = ...
List<X509Certificate> chain = ...
Collection<X509CRL> crls = ...

/* Construct a valid path. */
KeyStore anchors = KeyStore.getInstance(KeyStore.getDefaultType());
anchors.load(trustStoreInput, password);
X509CertSelector target = new X509CertSelector();
target.setCertificate(chain.get(0));
PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, target);
CertStoreParameters intermediates = new CollectionCertStoreParameters(chain)
params.addCertStore(CertStore.getInstance("Collection", intermediates));
CertStoreParameters revoked = new CollectionCertStoreParameters(crls);
params.addCertStore(CertStore.getInstance("Collection", revoked));
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
/* 
 * If build() returns successfully, the certificate is valid. More details 
 * about the valid path can be obtained through the PKIXBuilderResult.
 * If no valid path can be found, a CertPathBuilderException is thrown.
 */
PKIXBuilderResult r = (PKIXBuilderResult) builder.build(params);

An important thing to note is that if a path cannot be found, you don't get much information about the reason. This can be frustrating, but it is that way by design. In general, there are many potential paths. If they all fail for different reasons, how would the path builder decide what to report as the reason?