2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util.security;
21 import java.security.GeneralSecurityException;
22 import java.security.InvalidParameterException;
23 import java.security.KeyStore;
24 import java.security.KeyStoreException;
25 import java.security.Security;
26 import java.security.cert.CRL;
27 import java.security.cert.CertPathBuilder;
28 import java.security.cert.CertPathBuilderResult;
29 import java.security.cert.CertPathValidator;
30 import java.security.cert.CertStore;
31 import java.security.cert.Certificate;
32 import java.security.cert.CertificateException;
33 import java.security.cert.CollectionCertStoreParameters;
34 import java.security.cert.PKIXBuilderParameters;
35 import java.security.cert.X509CertSelector;
36 import java.security.cert.X509Certificate;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.Enumeration;
40 import java.util.concurrent.atomic.AtomicLong;
42 import org.eclipse.jetty.util.log.Log;
43 import org.eclipse.jetty.util.log.Logger;
46 * Convenience class to handle validation of certificates, aliases and keystores
48 * Allows specifying Certificate Revocation List (CRL), as well as enabling
49 * CRL Distribution Points Protocol (CRLDP) certificate extension support,
50 * and also enabling On-Line Certificate Status Protocol (OCSP) support.
52 * IMPORTANT: at least one of the above mechanisms *MUST* be configured and
53 * operational, otherwise certificate validation *WILL FAIL* unconditionally.
55 public class CertificateValidator
57 private static final Logger LOG = Log.getLogger(CertificateValidator.class);
58 private static AtomicLong __aliasCount = new AtomicLong();
60 private KeyStore _trustStore;
61 private Collection<? extends CRL> _crls;
63 /** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */
64 private int _maxCertPathLength = -1;
65 /** CRL Distribution Points (CRLDP) support */
66 private boolean _enableCRLDP = false;
67 /** On-Line Certificate Status Protocol (OCSP) support */
68 private boolean _enableOCSP = false;
69 /** Location of OCSP Responder */
70 private String _ocspResponderURL;
73 * creates an instance of the certificate validator
78 public CertificateValidator(KeyStore trustStore, Collection<? extends CRL> crls)
80 if (trustStore == null)
82 throw new InvalidParameterException("TrustStore must be specified for CertificateValidator.");
85 _trustStore = trustStore;
90 * validates all aliases inside of a given keystore
93 * @throws CertificateException
95 public void validate( KeyStore keyStore ) throws CertificateException
99 Enumeration<String> aliases = keyStore.aliases();
101 for ( ; aliases.hasMoreElements(); )
103 String alias = aliases.nextElement();
105 validate(keyStore,alias);
109 catch ( KeyStoreException kse )
111 throw new CertificateException("Unable to retrieve aliases from keystore", kse);
117 * validates a specific alias inside of the keystore being passed in
121 * @return the keyAlias if valid
122 * @throws CertificateException
124 public String validate(KeyStore keyStore, String keyAlias) throws CertificateException
126 String result = null;
128 if (keyAlias != null)
132 validate(keyStore, keyStore.getCertificate(keyAlias));
134 catch (KeyStoreException kse)
137 throw new CertificateException("Unable to validate certificate" +
138 " for alias [" + keyAlias + "]: " + kse.getMessage(), kse);
147 * validates a specific certificate inside of the keystore being passed in
151 * @throws CertificateException
153 public void validate(KeyStore keyStore, Certificate cert) throws CertificateException
155 Certificate[] certChain = null;
157 if (cert != null && cert instanceof X509Certificate)
159 ((X509Certificate)cert).checkValidity();
161 String certAlias = null;
164 if (keyStore == null)
166 throw new InvalidParameterException("Keystore cannot be null");
169 certAlias = keyStore.getCertificateAlias((X509Certificate)cert);
170 if (certAlias == null)
172 certAlias = "JETTY" + String.format("%016X",__aliasCount.incrementAndGet());
173 keyStore.setCertificateEntry(certAlias, cert);
176 certChain = keyStore.getCertificateChain(certAlias);
177 if (certChain == null || certChain.length == 0)
179 throw new IllegalStateException("Unable to retrieve certificate chain");
182 catch (KeyStoreException kse)
185 throw new CertificateException("Unable to validate certificate" +
186 (certAlias == null ? "":" for alias [" +certAlias + "]") + ": " + kse.getMessage(), kse);
193 public void validate(Certificate[] certChain) throws CertificateException
197 ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
198 for (Certificate item : certChain)
203 if (!(item instanceof X509Certificate))
205 throw new IllegalStateException("Invalid certificate type in chain");
208 certList.add((X509Certificate)item);
211 if (certList.isEmpty())
213 throw new IllegalStateException("Invalid certificate chain");
217 X509CertSelector certSelect = new X509CertSelector();
218 certSelect.setCertificate(certList.get(0));
220 // Configure certification path builder parameters
221 PKIXBuilderParameters pbParams = new PKIXBuilderParameters(_trustStore, certSelect);
222 pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList)));
224 // Set maximum certification path length
225 pbParams.setMaxPathLength(_maxCertPathLength);
227 // Enable revocation checking
228 pbParams.setRevocationEnabled(true);
230 // Set static Certificate Revocation List
231 if (_crls != null && !_crls.isEmpty())
233 pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(_crls)));
236 // Enable On-Line Certificate Status Protocol (OCSP) support
239 Security.setProperty("ocsp.enable","true");
241 // Enable Certificate Revocation List Distribution Points (CRLDP) support
244 System.setProperty("com.sun.security.enableCRLDP","true");
247 // Build certification path
248 CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams);
250 // Validate certification path
251 CertPathValidator.getInstance("PKIX").validate(buildResult.getCertPath(),pbParams);
253 catch (GeneralSecurityException gse)
256 throw new CertificateException("Unable to validate certificate: " + gse.getMessage(), gse);
260 public KeyStore getTrustStore()
265 public Collection<? extends CRL> getCrls()
271 * @return Maximum number of intermediate certificates in
272 * the certification path (-1 for unlimited)
274 public int getMaxCertPathLength()
276 return _maxCertPathLength;
279 /* ------------------------------------------------------------ */
281 * @param maxCertPathLength
282 * maximum number of intermediate certificates in
283 * the certification path (-1 for unlimited)
285 public void setMaxCertPathLength(int maxCertPathLength)
287 _maxCertPathLength = maxCertPathLength;
290 /* ------------------------------------------------------------ */
292 * @return true if CRL Distribution Points support is enabled
294 public boolean isEnableCRLDP()
299 /* ------------------------------------------------------------ */
300 /** Enables CRL Distribution Points Support
301 * @param enableCRLDP true - turn on, false - turns off
303 public void setEnableCRLDP(boolean enableCRLDP)
305 _enableCRLDP = enableCRLDP;
308 /* ------------------------------------------------------------ */
310 * @return true if On-Line Certificate Status Protocol support is enabled
312 public boolean isEnableOCSP()
317 /* ------------------------------------------------------------ */
318 /** Enables On-Line Certificate Status Protocol support
319 * @param enableOCSP true - turn on, false - turn off
321 public void setEnableOCSP(boolean enableOCSP)
323 _enableOCSP = enableOCSP;
326 /* ------------------------------------------------------------ */
328 * @return Location of the OCSP Responder
330 public String getOcspResponderURL()
332 return _ocspResponderURL;
335 /* ------------------------------------------------------------ */
336 /** Set the location of the OCSP Responder.
337 * @param ocspResponderURL location of the OCSP Responder
339 public void setOcspResponderURL(String ocspResponderURL)
341 _ocspResponderURL = ocspResponderURL;