//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util.security;
import java.security.GeneralSecurityException;
import java.security.InvalidParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Security;
import java.security.cert.CRL;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderResult;
import java.security.cert.CertPathValidator;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Convenience class to handle validation of certificates, aliases and keystores
Allows specifying Certificate Revocation List (CRL), as well as enabling
CRL Distribution Points Protocol (CRLDP) certificate extension support,
and also enabling On-Line Certificate Status Protocol (OCSP) support.
IMPORTANT: at least one of the above mechanisms *MUST* be configured and
operational, otherwise certificate validation *WILL FAIL* unconditionally.
/**
* Convenience class to handle validation of certificates, aliases and keystores
*
* Allows specifying Certificate Revocation List (CRL), as well as enabling
* CRL Distribution Points Protocol (CRLDP) certificate extension support,
* and also enabling On-Line Certificate Status Protocol (OCSP) support.
*
* IMPORTANT: at least one of the above mechanisms *MUST* be configured and
* operational, otherwise certificate validation *WILL FAIL* unconditionally.
*/
public class CertificateValidator
{
private static final Logger LOG = LoggerFactory.getLogger(CertificateValidator.class);
private static AtomicLong __aliasCount = new AtomicLong();
private KeyStore _trustStore;
private Collection<? extends CRL> _crls;
Maximum certification path length (n - number of intermediate certs, -1 for unlimited)
/**
* Maximum certification path length (n - number of intermediate certs, -1 for unlimited)
*/
private int _maxCertPathLength = -1;
CRL Distribution Points (CRLDP) support
/**
* CRL Distribution Points (CRLDP) support
*/
private boolean _enableCRLDP = false;
On-Line Certificate Status Protocol (OCSP) support
/**
* On-Line Certificate Status Protocol (OCSP) support
*/
private boolean _enableOCSP = false;
Location of OCSP Responder
/**
* Location of OCSP Responder
*/
private String _ocspResponderURL;
creates an instance of the certificate validator
Params: - trustStore – the truststore to use
- crls – the Certificate Revocation List to use
/**
* creates an instance of the certificate validator
*
* @param trustStore the truststore to use
* @param crls the Certificate Revocation List to use
*/
public CertificateValidator(KeyStore trustStore, Collection<? extends CRL> crls)
{
if (trustStore == null)
{
throw new InvalidParameterException("TrustStore must be specified for CertificateValidator.");
}
_trustStore = trustStore;
_crls = crls;
}
validates all aliases inside of a given keystore
Params: - keyStore – the keystore to validate
Throws: - CertificateException – if keystore error and unable to validate
/**
* validates all aliases inside of a given keystore
*
* @param keyStore the keystore to validate
* @throws CertificateException if keystore error and unable to validate
*/
public void validate(KeyStore keyStore) throws CertificateException
{
try
{
Enumeration<String> aliases = keyStore.aliases();
for (; aliases.hasMoreElements(); )
{
String alias = aliases.nextElement();
validate(keyStore, alias);
}
}
catch (KeyStoreException kse)
{
throw new CertificateException("Unable to retrieve aliases from keystore", kse);
}
}
validates a specific alias inside of the keystore being passed in
Params: - keyStore – the keystore to validate
- keyAlias – the keyalias in the keystore to valid with
Throws: - CertificateException – if keystore error and unable to validate
Returns: the keyAlias if valid
/**
* validates a specific alias inside of the keystore being passed in
*
* @param keyStore the keystore to validate
* @param keyAlias the keyalias in the keystore to valid with
* @return the keyAlias if valid
* @throws CertificateException if keystore error and unable to validate
*/
public String validate(KeyStore keyStore, String keyAlias) throws CertificateException
{
String result = null;
if (keyAlias != null)
{
try
{
validate(keyStore, keyStore.getCertificate(keyAlias));
}
catch (KeyStoreException kse)
{
LOG.debug("Unable to validate alias: {}", keyAlias, kse);
throw new CertificateException("Unable to validate certificate" +
" for alias [" + keyAlias + "]: " + kse.getMessage(), kse);
}
result = keyAlias;
}
return result;
}
validates a specific certificate inside of the keystore being passed in
Params: - keyStore – the keystore to validate against
- cert – the certificate to validate
Throws: - CertificateException – if keystore error and unable to validate
/**
* validates a specific certificate inside of the keystore being passed in
*
* @param keyStore the keystore to validate against
* @param cert the certificate to validate
* @throws CertificateException if keystore error and unable to validate
*/
public void validate(KeyStore keyStore, Certificate cert) throws CertificateException
{
Certificate[] certChain = null;
if (cert != null && cert instanceof X509Certificate)
{
((X509Certificate)cert).checkValidity();
String certAlias = null;
try
{
if (keyStore == null)
{
throw new InvalidParameterException("Keystore cannot be null");
}
certAlias = keyStore.getCertificateAlias((X509Certificate)cert);
if (certAlias == null)
{
certAlias = "JETTY" + String.format("%016X", __aliasCount.incrementAndGet());
keyStore.setCertificateEntry(certAlias, cert);
}
certChain = keyStore.getCertificateChain(certAlias);
if (certChain == null || certChain.length == 0)
{
throw new IllegalStateException("Unable to retrieve certificate chain");
}
}
catch (KeyStoreException kse)
{
LOG.debug("Unable to validate certificate", kse);
throw new CertificateException("Unable to validate certificate" +
(certAlias == null ? "" : " for alias [" + certAlias + "]") + ": " + kse.getMessage(), kse);
}
validate(certChain);
}
}
public void validate(Certificate[] certChain) throws CertificateException
{
try
{
ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
for (Certificate item : certChain)
{
if (item == null)
continue;
if (!(item instanceof X509Certificate))
{
throw new IllegalStateException("Invalid certificate type in chain");
}
certList.add((X509Certificate)item);
}
if (certList.isEmpty())
{
throw new IllegalStateException("Invalid certificate chain");
}
X509CertSelector certSelect = new X509CertSelector();
certSelect.setCertificate(certList.get(0));
// Configure certification path builder parameters
PKIXBuilderParameters pbParams = new PKIXBuilderParameters(_trustStore, certSelect);
pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList)));
// Set maximum certification path length
pbParams.setMaxPathLength(_maxCertPathLength);
// Enable revocation checking
pbParams.setRevocationEnabled(true);
// Set static Certificate Revocation List
if (_crls != null && !_crls.isEmpty())
{
pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(_crls)));
}
// Enable On-Line Certificate Status Protocol (OCSP) support
if (_enableOCSP)
{
Security.setProperty("ocsp.enable", "true");
}
// Enable Certificate Revocation List Distribution Points (CRLDP) support
if (_enableCRLDP)
{
System.setProperty("com.sun.security.enableCRLDP", "true");
}
// Build certification path
CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams);
// Validate certification path
CertPathValidator.getInstance("PKIX").validate(buildResult.getCertPath(), pbParams);
}
catch (GeneralSecurityException gse)
{
LOG.debug("Unable to validate certificate chain", gse);
throw new CertificateException("Unable to validate certificate: " + gse.getMessage(), gse);
}
}
public KeyStore getTrustStore()
{
return _trustStore;
}
public Collection<? extends CRL> getCrls()
{
return _crls;
}
Returns: Maximum number of intermediate certificates in
the certification path (-1 for unlimited)
/**
* @return Maximum number of intermediate certificates in
* the certification path (-1 for unlimited)
*/
public int getMaxCertPathLength()
{
return _maxCertPathLength;
}
Params: - maxCertPathLength – maximum number of intermediate certificates in
the certification path (-1 for unlimited)
/**
* @param maxCertPathLength maximum number of intermediate certificates in
* the certification path (-1 for unlimited)
*/
public void setMaxCertPathLength(int maxCertPathLength)
{
_maxCertPathLength = maxCertPathLength;
}
Returns: true if CRL Distribution Points support is enabled
/**
* @return true if CRL Distribution Points support is enabled
*/
public boolean isEnableCRLDP()
{
return _enableCRLDP;
}
Enables CRL Distribution Points Support
Params: - enableCRLDP – true - turn on, false - turns off
/**
* Enables CRL Distribution Points Support
*
* @param enableCRLDP true - turn on, false - turns off
*/
public void setEnableCRLDP(boolean enableCRLDP)
{
_enableCRLDP = enableCRLDP;
}
Returns: true if On-Line Certificate Status Protocol support is enabled
/**
* @return true if On-Line Certificate Status Protocol support is enabled
*/
public boolean isEnableOCSP()
{
return _enableOCSP;
}
Enables On-Line Certificate Status Protocol support
Params: - enableOCSP – true - turn on, false - turn off
/**
* Enables On-Line Certificate Status Protocol support
*
* @param enableOCSP true - turn on, false - turn off
*/
public void setEnableOCSP(boolean enableOCSP)
{
_enableOCSP = enableOCSP;
}
Returns: Location of the OCSP Responder
/**
* @return Location of the OCSP Responder
*/
public String getOcspResponderURL()
{
return _ocspResponderURL;
}
Set the location of the OCSP Responder.
Params: - ocspResponderURL – location of the OCSP Responder
/**
* Set the location of the OCSP Responder.
*
* @param ocspResponderURL location of the OCSP Responder
*/
public void setOcspResponderURL(String ocspResponderURL)
{
_ocspResponderURL = ocspResponderURL;
}
}