Skip to content
OcspLookup.java 10.9 KiB
Newer Older
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this
 * file except in compliance with the License. You may obtain
 * a copy of the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 *
 * The Original Code is Java RASP toolkit.
 *
 * The Initial Developer of the Original Code is Lenio. Portions
 * created by Lenio are Copyright (C) 2007 Danish National IT and
 * Telecom Agency (http://www.itst.dk). All Rights Reserved.
 *
 * Contributor(s):
 *   Tommy Dejbjerg Pedersen, Lenio
 *   Hans Guldager Knudsen, Lenio
 *   Patrik Johansson, Accenture
 *   Dennis Søgaard, Accenture
 *   Martin Bentzen, Accenture
 *   Jesper Jensen, Avanade
 *   Ramzi Fadel, Avanade
 *   Christian Pedersen, Accenture
 *   Morten Hougesen, Accenture
 *   Mikkel Hippe Brun, ITST
 *   Finn Hartmann Jordal, ITST
 *   Christian Lanng, ITST
package dk.gov.oiosi.security.revocation.ocsp;

import dk.certifikat.ocsp.client.OcspCertificateResponse;
import dk.certifikat.ocsp.client.OcspCertificateResponse.OcspCertificateStatus;
import dk.certifikat.ocsp.client.OcspClient;
import dk.certifikat.ocsp.client.OcspRegistry;
import dk.certifikat.ocsp.client.OcspSystemException;
import dk.gov.oiosi.common.OutVariable;
import dk.gov.oiosi.common.cache.ICache;
import dk.gov.oiosi.configuration.CacheConfig;
import dk.gov.oiosi.configuration.CacheFactory;
hknudsen's avatar
hknudsen committed
import dk.gov.oiosi.configuration.ConfigurationException;
import dk.gov.oiosi.configuration.ConfigurationHandler;
import dk.gov.oiosi.security.CertificateHandlingException;
import dk.gov.oiosi.security.revocation.IRevocationLookup;
import dk.gov.oiosi.security.revocation.RevocationException;
import dk.gov.oiosi.security.revocation.RevocationResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.ocsp.OCSPException;

import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
/**
 * Class for checking certificate revocation status against an OCSP server.
 */
public class OcspLookup implements IRevocationLookup {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    /**
     * local log.
     */
    private Log log = LogFactory.getLog(OcspLookup.class);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    /**
     * OCSP configuration.
     */
    private OcspConfig configuration;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    /**
     * Root certificate
     */
    private List<X509Certificate> rootcertList = null;
    // Set cache time to one hour
    private static final ICache<X500Principal, RevocationResponse> ocspCache = CacheFactory.getInstance().getOcspLookupCache();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed


    /**
     * Gets the configuration of the lookup client.
     *
     * @return OCSP configuration
     */
    public OcspConfig getConfiguration() {
        return configuration;
    }

    /**
     * Instantiates OcspLookup and loads the OCES default root certificate.
     *
     * @param conf Configuration parameters
     */
    public OcspLookup(OcspConfig conf) {
        configuration = conf;
        rootcertList = null;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }

    /**
     * Creates a new OcspLookup class
     *
     * @param conf                   The configuration to use in this lookup
     * @param defaultRootCertificate The root certificate
     */
    public OcspLookup(OcspConfig conf, X509Certificate defaultRootCertificate) {
        configuration = conf;
        //rootcert = defaultRootCertificate;
        rootcertList = new ArrayList<X509Certificate>();
        rootcertList.add(defaultRootCertificate);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }

    /**
     * Creates a new OcspLookup class
     *
     * @param conf                       The configuration to use in this lookup
     * @param defaultRootCertificateList The root certificate
     */
    public OcspLookup(OcspConfig conf, ArrayList<X509Certificate> defaultRootCertificateList) {
        configuration = conf;
        rootcertList = defaultRootCertificateList;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }

    /**
     * Default constructor. Attempts to load configuration from configuration
     * file.
     *
     * @throws ConfigurationException On error...
     */
    public OcspLookup() throws ConfigurationException {
        try {
            configuration = ConfigurationHandler.getInstance().getOcspConfig();
        } catch (URISyntaxException e) {
            throw new ConfigurationException(e.getMessage());
        }
    }

    /**
     * Checks a certificate status on a ocsp server.
     *
     * @param certificate The certificate to check
     * @return The RevocationResponse object that contains the result
     * @throws RevocationException
     */
    public RevocationResponse checkCertificate(X509Certificate certificate) throws RevocationException {
        OutVariable<RevocationResponse> value = new OutVariable<RevocationResponse>();
        if (ocspCache.tryGetValue(certificate.getSubjectX500Principal(), value)) {
            return value.getVariable();
        }

        // The response was not in the cache - we must validete the certificate our self
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        RevocationResponse response = new RevocationResponse();
        // default to false
        response.setIsValid(false);
        String serverUrl;

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        try {
            log.debug("Setting up OCSP client:");
            if (rootcertList  == null) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // get the rootcert(s) from configurationsettings
                rootcertList = configuration.getDefaultOcesRootCertificateCollectionFromStore()  ;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            }

            if (rootcertList.size() <= 0) {
                // no root certificate is defined
                // we can't proceed
                throw new RevocationException("No root certificate has been defined.");
            }

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            if (configuration.getServerUrl() == null) {
                // Use OCSP server specified in certificate
                serverUrl = GetServerUriFromCertificate(certificate);
            } else {
                // Use OCSP server specified in configuration
                serverUrl = configuration.getServerUrl();
            }
        } catch (RevocationException e) {
            throw e;
        } catch (CertificateHandlingException e) {
            log.error("Error handling the certificate" + e.getMessage());
            throw new RevocationException(e);
        } catch (Exception e) {
            log.error("Error initailizing validator : " + e.getMessage(), e);
            throw new RevocationException(e);
        }

        OcspCertificateResponse ocspCertificateResponse = null;
        OcspClient client = null;
        boolean validationNotFinish = true;
        int rootCertificateIndex = 0;

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        while (validationNotFinish && rootCertificateIndex < rootcertList.size()) {
            try {
                log.debug("start validation certificate");
                client = OcspRegistry.getOcspClient();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                client.setResponderUrl(serverUrl);
                client.setCaCert(rootcertList.get(rootCertificateIndex));

                // Try validation if this is the correct root certificate
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                BigInteger[] serNos = {certificate.getSerialNumber()};

                log.debug("Getting certificate status for:" + serNos[0]);
                ocspCertificateResponse = client.getCertificateStatus(serNos);

                log.trace("Validating certificate...");
                client.validateCerts(ocspCertificateResponse);

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                if (ocspCertificateResponse.isOk()) {
                    OcspCertificateStatus status = ocspCertificateResponse.getCertificateStatus(serNos[0]);

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    if ("Good".equals(status.toString())) {
                        log.debug("Status of cert " + certificate.getSubjectDN() + " : [" + status + "]");
                        response.setIsValid(true);
                        response.setNextUpdate(ocspCertificateResponse.getNextUpdate());
                        // all okay, do not check other root certificate
                        validationNotFinish = false;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    } else {
                        // The used root certificate is not valid, check the next one
                        // The rootCertificateIndex is increased at the buttom of this try-catch

                        log.warn("Certificate validation failed " + serNos[0] + " : [" + status + "]");
                    }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                } else {
                    // The certificateResponse is invalid
                    log.error("Response returned an error: " + ocspCertificateResponse.getError());
                    throw new RevocationException("Response returned an error: " + ocspCertificateResponse.getError());
                }

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            } catch (RevocationException e) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            } catch (OcspSystemException e) {
                log.debug("Error validating certificate : " + e.getMessage(), e);
                //throw new RevocationException(e);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            } catch (OCSPException e) {
                log.error("Error validating certificate : " + e.getMessage(), e);
                throw new RevocationException(e);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            } catch (Exception e) {
                log.error("Error validator certificate : " + e.getMessage(), e);
                throw new RevocationException(e);
            }

            rootCertificateIndex++;
        }

        log.debug("Validating X509 certificate finished.");

        // add the result to cache
        ocspCache.set(certificate.getSubjectX500Principal(), response);

        return response;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }

    public String GetServerUriFromCertificate(X509Certificate certificate) throws RevocationException {
        try {
            byte[] ext = certificate.getExtensionValue(X509Extensions.AuthorityInfoAccess.getId());
            ASN1InputStream aIn = new ASN1InputStream(ext);
            aIn = new ASN1InputStream(((ASN1OctetString) aIn.readObject()).getOctets());
            ASN1Encodable object = (ASN1Encodable) aIn.readObject();
            AuthorityInformationAccess auth = AuthorityInformationAccess.getInstance(object);
            AccessDescription[] acc = auth.getAccessDescriptions();
            return acc[0].getAccessLocation().toString().substring(3);
        } catch (IOException e) {
            throw new RevocationException(e);
        }
    }
}