Skip to content
OcspLookup.java 48.9 KiB
Newer Older
dlk-truelink's avatar
dlk-truelink committed
/*
 * 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.
package dk.gov.oiosi.security.revocation.ocsp;
dlk-truelink's avatar
dlk-truelink committed
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
dlk-truelink's avatar
dlk-truelink committed
import java.net.URISyntaxException;
import java.net.URL;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.security.auth.x500.X500Principal;

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.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.ocsp.*;

import dk.gov.oiosi.common.OutVariable;
import dk.gov.oiosi.common.cache.ICache;
import dk.gov.oiosi.configuration.CacheFactory;
import dk.gov.oiosi.configuration.ConfigurationException;
import dk.gov.oiosi.configuration.ConfigurationHandler;
import dk.gov.oiosi.security.CertificateHandlingException;
import dk.gov.oiosi.security.oces.CertificateUtil;
import dk.gov.oiosi.security.revocation.IRevocationLookup;
import dk.gov.oiosi.security.revocation.RevocationException;
import dk.gov.oiosi.security.revocation.RevocationResponse;
import dk.gov.oiosi.security.revocation.crl.CrlLookup;
dlk-truelink's avatar
dlk-truelink committed

/**
 * Class for checking certificate revocation status against an OCSP server.
 */
dlk-truelink's avatar
dlk-truelink committed
/*
 * TODO DLK: A lot of code in this class uses deprecated API. We need to review
 * it, usually it is a bad sign.
 */
@SuppressWarnings("deprecation")
public class OcspLookup implements IRevocationLookup {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    private Log log = LogFactory.getLog(OcspLookup.class);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    private OcspConfig configuration;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    /**
     * Root certificate
     */
    private HashMap<String, X509Certificate> rootCertificateMap;
    /**
     * The ocsp cache
     */
    // Set cache time to one hour
    private final ICache<X500Principal, RevocationResponse> ocspCache = CacheFactory.getInstance().getOcspLookupCache();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed

    private CertificateUtil certificateUtil;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed

    /**
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
     * Default constructor. Attempts to load configuration from configuration
     * file.
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     *
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
     * @throws ConfigurationException On error...
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     */
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
    public OcspLookup() throws ConfigurationException {
        try {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            configuration = ConfigurationHandler.getInstance().getOcspConfig();
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
        } catch (URISyntaxException e) {
            throw new ConfigurationException(e.getMessage());
        }

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        rootCertificateMap = new HashMap<String, X509Certificate>();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            List<X509Certificate> list = configuration.getDefaultOcesRootCertificateCollectionFromStore();
            for (X509Certificate rootCertificate : list) {
                rootCertificateMap.put(rootCertificate.getIssuerDN().getName(), rootCertificate);
        } catch (CertificateHandlingException e) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            log.error(e.getMessage(), e);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        certificateUtil = new CertificateUtil();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }

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

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        rootCertificateMap = new HashMap<String, X509Certificate>();

        try {
            List<X509Certificate> list = this.configuration.getDefaultOcesRootCertificateCollectionFromStore();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            for (X509Certificate rootCertificate : list) {
                this.rootCertificateMap.put(rootCertificate.getIssuerDN().getName(), rootCertificate);
            }
        } catch (CertificateHandlingException e) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            log.error(e.getMessage(), e);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        certificateUtil = new CertificateUtil();
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) {
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
        this.configuration = conf;
        this.rootCertificateMap = new HashMap<String, X509Certificate>();
        //this.rootcertList = new ArrayList<X509Certificate>();
        this.rootCertificateMap.put(defaultRootCertificate.getIssuerDN().getName(), defaultRootCertificate);
        this.certificateUtil = new CertificateUtil();
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) {
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
        this.configuration = conf;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        rootCertificateMap = new HashMap<String, X509Certificate>();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        for (X509Certificate rootCertificate : defaultRootCertificateList) {
            rootCertificateMap.put(rootCertificate.getIssuerDN().getName(), rootCertificate);
        }

        this.certificateUtil = new CertificateUtil();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    /**
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
     * Gets the configuration of the lookup client.
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     *
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
     * @return OCSP configuration
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     */
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
    public OcspConfig getConfiguration() {
        return this.configuration;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    /**
     * 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>();
        RevocationResponse revocationResponse;
        if (this.ocspCache.tryGetValue(certificate.getSubjectX500Principal(), value)) {
            // response already in cache.
            // Check if the response still is valid
            Date now = new Date();
            revocationResponse = value.getVariable();
            if (revocationResponse.getNextUpdate().before(now)) {
                // the cached value is to old
                // get new value
                revocationResponse = this.checkCertificateAsync(certificate);

                // the cached value is to old
                // remove it from the cache
                /*this.ocspCache.remove(certificate.getSubjectX500Principal());

                // get new value
                revocationResponse = this.checkCertificateOnline(certificate);

                if(revocationResponse != null)
                {
                    // put revocationResponse in cache
                    ocspCache.set(certificate.getSubjectX500Principal(), revocationResponse);
            // response is not in cache
            revocationResponse = this.checkCertificateAsync(certificate);
            /*if(revocationResponse != null)
            {
                // put revocationResponse in cache
                ocspCache.set(certificate.getSubjectX500Principal(), revocationResponse);
            } */
        }

        return revocationResponse;
    }

    public RevocationResponse checkCertificateAsync(X509Certificate certificate) throws RevocationException {
        // Aync not implemented - it is in c#
        return this.revocationResponse(certificate);
    }

    public RevocationResponse revocationResponse(X509Certificate certificate) throws RevocationException {
        // this method can be call requsiv, so check the cache first
        RevocationResponse revocationResponse = null;
        OutVariable<RevocationResponse> value = new OutVariable<RevocationResponse>();

        if (this.ocspCache.tryGetValue(certificate.getSubjectX500Principal(), value)) {
            // response already in cache.
            // Check if the response still is valid
            Date now = new Date();
            revocationResponse = value.getVariable();
            if (revocationResponse.getNextUpdate().before(now)) {
                // the cached value is to old
                // get new value
                revocationResponse = this.revocationResponseOnline(certificate);
            // respons is not in cache
            revocationResponse = this.revocationResponseOnline(certificate);
        }
     * Checks a certificate status on a ocsp server
     *
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     * @param x509Certificate - The certificate to check.
     * @return The RevocationResponse object that contains the result
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     * @throws RevocationException On error...
     */
    public RevocationResponse revocationResponseOnline(X509Certificate x509Certificate) throws RevocationException {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        // The response was not in the cache - we must validate the certificate our self
        if (x509Certificate == null) {
            throw new RevocationException("Certificate is null");
        }

        X509Certificate issuerX509Certificate = this.findIssuerCertificate(x509Certificate);

        if (issuerX509Certificate == null) {
            throw new RevocationException("Certificate '"+x509Certificate.getSubjectX500Principal()+"'is not trusted, as issuer could not be identified");
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        RevocationResponse revocationResponse;
        if (x509Certificate.getIssuerDN().getName().equals(issuerX509Certificate.getSubjectX500Principal().getName())) {
            throw new RevocationException("Certificate not trusted, as the certificate is self signed");
        } else {
            revocationResponse = this.revocationResponseOnline(x509Certificate, issuerX509Certificate);

            if (revocationResponse != null) {
                //if (revocationResponse.Exception == null){
                // no exception recorded
                if (revocationResponse.isValid()) {
                    // now we know the certificate is valid.
                    // if the issuer is a trusted root certificate, all is good
                    if (this.rootCertificateMap.containsKey(issuerX509Certificate.getIssuerDN().getName())) {
                        // the root certificate is trusted, so the RevocationResponse can be put on the cache
                        this.ocspCache.add(x509Certificate.getSubjectX500Principal(), revocationResponse);
                        // we do not yet know if the certificate is valid.
                        // the certificate migth be good, but if the issueing certificate is revoked,
                        // then the certificate should also be revoked.
                        // Validate the issuer certificate
                        // this is required, because certificate can have a chain that is longer then 2

                        // The only problem is, that we can not ocsp validate the intermiddel certificate (the issuer certificate).
                        // acording to DanID - that certificate can only be validated with CRL
                        // Note : The crl list will be/should be very short. Only containing the issuer certificate that has been revoked.
                        //        A good guess is that there at all time will be most 10 issuer certificate, so the list of revoked issuer certificate is short.

                        List<String> issuerUrl = this.getAuthorityInformationAccessOcspUrl(issuerX509Certificate);
                        RevocationResponse issuerRevocationResponse;

                        if (issuerUrl.size() > 0) {
                            // hey, wow some url exist - lets use that
                            // don't thing this will ever happens anyway
                            issuerRevocationResponse = this.revocationResponse(issuerX509Certificate);
                            // we need to validate with crl instead
                            // It does not contain the Authority Info Access, containng the rl to where the certificate must be validated
                            // We must therefore gues, that the certificate is valid.
                            CrlLookup crlLookupClient = new CrlLookup();
                            issuerRevocationResponse = crlLookupClient.checkCertificate(issuerX509Certificate);
                        }

                        // now to handle the issuerRevocationResponse
                        if (issuerRevocationResponse != null) {
                            // the issuer certificate is validated, the validity of the issuer certificate
                            // is copied to the revocationResponse
                            revocationResponse.setIsValid(issuerRevocationResponse.isValid());
                            revocationResponse.setIsValid(false);
                            //revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("The issueing certificate could not be validated.");
                        }

                        // update the cache
                        this.ocspCache.add(x509Certificate.getSubjectX500Principal(), revocationResponse);
                    }
                    // the certificate is Not valid
                    // no need to check the issuer certificate
                    this.ocspCache.add(x509Certificate.getSubjectX500Principal(), revocationResponse);
                }
                // some exception returned.
                // do not add to cache
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            }
    private RevocationResponse revocationResponseOnline(X509Certificate serverX509Certificate, X509Certificate issuerX509Certificate) throws RevocationException {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        if (serverX509Certificate == null) {
            throw new RevocationException("Server certificate is null");
        }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        RevocationResponse revocationResponse;
        try {
            // create BouncyCastle certificates
            //X509CertificateParser certParser = new X509CertificateParser();
            //Org.BouncyCastle.X509.X509Certificate serverX509Certificate = certParser.ReadCertificate(serverX509Certificate2.RawData);
            // 1. Get server url
            List<String> urlList = this.getAuthorityInformationAccessOcspUrl(serverX509Certificate);
            if (urlList.size() == 0) {
                throw new RevocationException("No OCSP url found in ee certificate.");
            }
            // we always validate against the first defined url
            String url = urlList.get(0);
            revocationResponse = this.revocationResponseOnline(serverX509Certificate, issuerX509Certificate, url);
        } catch (OCSPException e) {
            //e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            throw new RevocationException(e);
        }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed

        return revocationResponse;
    }
    public RevocationResponse revocationResponseOnline(X509Certificate serverX509Certificate, X509Certificate issuerX509Certificate, String url) throws RevocationException, OCSPException {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        if (serverX509Certificate == null) {
            throw new RevocationException("Server certificate is null");
        }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        if (issuerX509Certificate == null) {
            throw new RevocationException("Issuer certificate for server certificate not identified");
        }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        RevocationResponse revocationResponse;
        try {
            // 1. Generate request
            OCSPReq req = this.generateOcspRequest(issuerX509Certificate, serverX509Certificate.getSerialNumber());

            // 2. make binary request online
            byte[] binaryResp = this.postData(req, url);

            //3. check result
            revocationResponse = this.processOcspResponse(serverX509Certificate, binaryResp);
        } catch (NoSuchProviderException e) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            log.error(e.getMessage(), e);
            throw new RevocationException(e);
        } catch (IOException e) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            log.error(e.getMessage(), e);
            throw new RevocationException(e);
        }

        /*  try {
      log.debug("Setting up OCSP client:");
      if (rootcertList  == null) {
          // get the rootcert(s) from configurationsettings
          rootcertList = configuration.getDefaultOcesRootCertificateCollectionFromStore()  ;
      }
      if (rootcertList.size() <= 0) {
          // no root certificate is defined
          // we can't proceed
          // hack
          //rootcertList = null;

         throw new RevocationException("No root certificate has been defined.");
      }  */
        /* if (configuration.getServerUrl() == null) {
       // Use OCSP server specified in certificate
       //serverUrl = GetServerUriFromCertificate(certificate);
   } else {
       // Use OCSP server specified in configuration
       // only valid when testing
       // the url for the ocsp server, variate.
       // It is based on the certificate version, and whether it is a live or test certificate
       // oces1 test :
       // oces1 live :
       // oces2 test :  http://ocsp.systemtest8.trust2408.com/responder
       // oces2 live :
       //serverUrl = configuration.getServerUrl();
       throw new RevocationException("No root certificate has been defined.");
   }     */

        /*  if(serverUrl == null || serverUrl.equalsIgnoreCase(""))
        {
            throw new RevocationException("No srever url has been defined.");
        }*/
        /*} catch (RevocationException e) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            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);
        response = this.isCertificateRevoked(certificate,  rootcertList);    */
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed


        /* OCSPCertificateRevocationChecker checker;
        checker = new OCSPCertificateRevocationChecker();

        List<X509Certificate> certificates = new LinkedList<X509Certificate> ();
        certificates.add(certificate);



        OcesCertificate focesCertificate = null;
        try {
            focesCertificate = OcesCertificateFactory.getInstance().generate(certificates);
        }
        catch (TrustCouldNotBeVerifiedException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        boolean isValid = checker.isRevoked(focesCertificate);


        OCSPReqGenerator gen = new OCSPReqGenerator();

            */

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

    while (validationNotFinish && rootCertificateIndex < rootcertList.size()) {
        try {
            log.debug("start validation certificate");
            client = OcspRegistry.getOcspClient();
            client.setResponderUrl(serverUrl);
            client.setCaCert(rootcertList.get(rootCertificateIndex));

            // Try validation if this is the correct root certificate
            BigInteger[] serNos = {certificate.getSerialNumber()};

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

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

            if (ocspCertificateResponse.isOk()) {
                OcspCertificateStatus status = ocspCertificateResponse.getCertificateStatus(serNos[0]);

                if ("Good".equals(status.toString())) {
                    log.debug("Status of cert " + certificate.getSubjectDN() + " : [" + status + "]");
                    response = new RevocationResponse();
                    response.setIsValid(true);
                    response.setNextUpdate(ocspCertificateResponse.getNextUpdate());
                    // all okay, do not check other root certificate
                    validationNotFinish = false;
                } else if ("Revoked".equals(status.toString())) {
                    log.debug("Status of cert " + certificate.getSubjectDN() + " : [" + status + "]");
                    response = new RevocationResponse();
                    response.setIsValid(false);
                    response.setNextUpdate(ocspCertificateResponse.getNextUpdate());
                }
                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 + "]");
                }
            } else {
                // The certificateResponse is invalid
                log.error("Response returned an error: " + ocspCertificateResponse.getError());
                throw new RevocationException("Response returned an error: " + ocspCertificateResponse.getError());
        } catch (RevocationException e) {
            throw e;
        } catch (OcspSystemException e) {
            log.debug("Error validating certificate : " + e.getMessage(), e);
            //throw new RevocationException(e);
        } catch (OCSPException e) {
            log.error("Error validating certificate : " + e.getMessage(), e);
            throw new RevocationException(e);
        } catch (Exception e) {
            log.error("Error validator certificate : " + e.getMessage(), e);
            throw new RevocationException(e);
        rootCertificateIndex++;
    }
        */
        /* log.debug("Validating X509 certificate finished.");

// if response is different from null, a validation was succesfull
if(response == null)
{
    throw new RevocationException("Valid revocation response was not received.");
}
return response;       */
        return revocationResponse;
    }

    private OCSPReq generateOcspRequest(X509Certificate rootX509Certificate, BigInteger serialNumber) throws OCSPException {
        CertificateID certificateID = new CertificateID(CertificateID.HASH_SHA1, rootX509Certificate, serialNumber);
        return this.generateOcspRequest(certificateID);
    private OCSPReq generateOcspRequest(CertificateID id) throws OCSPException {
        OCSPReqGenerator ocspRequestGenerator = new OCSPReqGenerator();
        ocspRequestGenerator.addRequest(id);
        /*
     BigInteger nonce = BigInteger.ValueOf(new DateTime().Ticks);
     ArrayList oids = new ArrayList();
     Hashtable values = new Hashtable();
     oids.Add(OcspObjectIdentifiers.PkixOcsp);
     Asn1OctetString asn1 = new DerOctetString(new DerOctetString(new byte[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1 }));
     values.Add(OcspObjectIdentifiers.PkixOcsp, new Org.BouncyCastle.Asn1.X509.X509Extension(false, asn1));
     X509Extensions x509Extensions = new X509Extensions(oids, values);
     ocspRequestGenerator.SetRequestExtensions(x509Extensions);
     OcspReq ocspReq = ocspRequestGenerator.Generate();*/

        OCSPReq req = ocspRequestGenerator.generate();
        return req;
    }

    private RevocationResponse processOcspResponse(X509Certificate serverX509Certificate, byte[] binaryResp) throws IOException, OCSPException, RevocationException, NoSuchProviderException {
        OCSPResp ocspResponse = new OCSPResp(binaryResp);
        RevocationResponse revocationResponse = new RevocationResponse();

        switch (ocspResponse.getStatus()) {
            case OCSPResponseStatus.SUCCESSFUL: {
                Object responseObject = ocspResponse.getResponseObject();

                if (!(responseObject instanceof BasicOCSPResp)) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    throw new IllegalStateException("ocsp response is of unexpected type");
                BasicOCSPResp basicResp = (BasicOCSPResp) responseObject;
                /* check condition:
                 The certificate identified in a received response corresponds to
                 that which was identified in the corresponding request;
                 */
                SingleResp[] responses = basicResp.getResponses();
                if (responses.length != 1) {
                    throw new IllegalStateException("unexpected number of responses received");
                }

                SingleResp singleResp = responses[0];
                if (!serverX509Certificate.getSerialNumber().equals(singleResp.getCertID().getSerialNumber())) {
                    throw new RevocationException("Serial number mismatch problem");
                }

                X509Certificate ocspCertificate = this.findOcspClientCertificate(basicResp.getCerts("BC"));

                /* check condition
                   The signature on the response is valid;
                   The chain is validated other check
                */
                //if(!this.verifyOcspCertificateChain(certificate, ocspCertificate)) {
                //	throw new IllegalStateException("Certificate used to sign OCSP Response could not be verified");
                //}

                /* check the signature on the ocsp response */
                if (!basicResp.verify(ocspCertificate.getPublicKey(), "BC")) {
                    throw new RevocationException("signature validation failed for ocsp response");
                }

                if (!canSignOcspResponses(ocspCertificate)) {
                    throw new RevocationException("ocsp signing certificate has not been cleared for ocsp response signing");
                }

                /* check expiry of the signing certificate */
                if (!certificateValid(ocspCertificate)) {
                    throw new IllegalStateException("ocsp signing certificate is not valid");
                }

                Object certStatus = singleResp.getCertStatus();

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // Now to validate the actual revocation status
                if (certStatus == null) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    // according to org.openoces.ooapi.utils.ocsp.ResponseParser.cs (DanID test code)
                    // in the TU11, method SerialNumberInResponseIsNotRevoked(..),
                    // when the certificateStatus is empty, all is okay - not revoked
                    // no revocation data exist - the certificate must be valid
                    revocationResponse.setIsValid(true);
                    revocationResponse.setNextUpdate(singleResp.getNextUpdate());
                } else if (certStatus == org.bouncycastle.ocsp.CertificateStatus.GOOD) {
                    // this is the expected certificateStatus for valid certificates
                    // however if the status is good the certStatus is null
                    revocationResponse.setIsValid(true);
                    revocationResponse.setNextUpdate(singleResp.getNextUpdate());
                } else if (certStatus instanceof org.bouncycastle.ocsp.RevokedStatus) {
                    revocationResponse.setIsValid(false);
                    revocationResponse.setNextUpdate(singleResp.getNextUpdate());
                } else if (certStatus instanceof org.bouncycastle.ocsp.UnknownStatus) {
                    throw new RevocationException("ocsp response indicates unknown certificate status");
                } else {
                    throw new RevocationException("unknown status");
                }

                break;
            }

            case OCSPResponseStatus.INTERNAL_ERROR:
            case OCSPResponseStatus.MALFORMED_REQUEST:
            case OCSPResponseStatus.SIG_REQUIRED:
            case OCSPResponseStatus.TRY_LATER:
            case OCSPResponseStatus.UNAUTHORIZED:
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // all other then success is.
                new RevocationException("OCSPResponseStatus not valid!");
            }
        }

        return revocationResponse;
    }

    private byte[] postData(OCSPReq req, String url) throws RevocationException {
        try {
            HttpURLConnection conn = postRequest(req.getEncoded(), url);

            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                log.warn("http response code from OCSP request was: " + conn.getResponseCode());
                throw new RevocationException("http response code from OCSP request was: " + conn.getResponseCode());
            }

            int len = conn.getContentLength();

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            return this.readResponse(conn, len);
        } catch (IOException e) {
            log.error("Error doing OCSP lookup by url "+url+": " + e.getMessage(), e);
            throw new RevocationException(e);
        }
    }

    private HttpURLConnection postRequest(byte[] bs, String responderURL) throws IOException {
        HttpURLConnection conn = this.setupHttpConnectionForPost(bs.length, responderURL);
        OutputStream os = conn.getOutputStream();
        os.write(bs);
        conn.connect();
        os.close();
        return conn;
    }

    private HttpURLConnection setupHttpConnectionForPost(int contentLength, String responderURL) throws IOException {
        HttpURLConnection conn;
        conn = (HttpURLConnection) new URL(responderURL).openConnection();
        conn.setAllowUserInteraction(false);
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Length", "" + contentLength);
        conn.setRequestProperty("Content-Type", "application/ocsp-request");
        return conn;
    }

    private byte[] readResponse(HttpURLConnection conn, int len) throws IOException {
        InputStream is = conn.getInputStream();
        byte[] respData = new byte[len];
        int bRead = 0;

        do {
            bRead += is.read(respData, bRead, Math.min(1024, len - bRead));
        } while (bRead != len);

        is.close();
        conn.disconnect();
        return respData;
    }

    public X509Certificate findIssuerCertificate(X509Certificate certificate) {
        // tree posibilities
        // 1 - A root certificate - exist in root list
        // 2 - A root certificate not trusted - not in list
        // 3 - Issuer certificate, can can be downloaded
        X509Certificate issuerCertificate = null;

        String key = certificate.getIssuerDN().getName();
        if (this.rootCertificateMap.containsKey(key)) {
            // root certificate is in the trusted key store
            // this is possible 1
            issuerCertificate = this.rootCertificateMap.get(key);
            // So this is possible 2 or 3
            // 2 - A root certificate not trusted - not in list
            // 3 - Issuer certificate, can can be downloaded
            // So we will try to download the certificate
            issuerCertificate = this.certificateUtil.downloadIssuerCert(certificate);

            // if ( issuerCertificate== null){
            // The issuer certificate is a root certificate , but not trusted (oces1)
            // Possible 2
            //} else {
            //  The issuer certificate is a issuerCertificate (oces2)
            //  Possible 3
            // }
        }

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

Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
    /*
    public OCSPResp sendOCSPRequestPOST(String postURL, X509Certificate certificate) throws CryptoCoreOCSPException
    {

        OCSPResp ocspResp = null;

        try
        {
        	BASE64Encoder encoder=new BASE64Encoder();
        	String certBase64 = encoder.encodeBuffer(certificate.getEncoded());

            URL url = new URL(postURL);
            URLConnection con = url.openConnection();
            con.setReadTimeout(10000);
            con.setConnectTimeout(10000);
            con.setAllowUserInteraction(false);
            con.setUseCaches(false);
            con.setDoOutput(true);
            con.setDoInput(true);

            con.setRequestProperty("Content-Type", "application/octet-stream");
            OutputStream os = con.getOutputStream();
            os.write(certBase64.getBytes());
            os.close();

            byte[] resp64 = OS.inputStreamToByteArray(con.getInputStream());
            byte[] bresp = Base64.decode(resp64);

            if (bresp.length==0)
            	ocspResp = new OCSPResp(new OCSPResponse(null));
            else
            	ocspResp = new OCSPResp(bresp);
        }
        catch (CertificateEncodingException cee)
        {
            throw new CryptoCoreOCSPException(
                    "Can not send OCSP request. Certificate encoding is not valid", cee);
        }
        catch (IOException ioe)
        {
            throw new CryptoCoreOCSPException("Can not recover response from server " + postURL,
                    ioe);
        }

        return ocspResp;
    }       */

    public String GetServerUriFromCertificate(X509Certificate certificate) throws RevocationException {
        // not optimal implementation - could be improved - look at danIds OOAPI TU11
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        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);
        }
    }
    public List<String> getAuthorityInformationAccessOcspUrl(X509Certificate x509Certificate) {
        List<String> ocspUrls = new ArrayList<String>();

        try {

            byte[] extBytes = x509Certificate.getExtensionValue(X509Extensions.AuthorityInfoAccess.getId());
            ASN1InputStream extensionAns1InputStream = new ASN1InputStream(extBytes);
            ASN1OctetString ans1OctetString = (ASN1OctetString) extensionAns1InputStream.readObject();
            byte[] octetsBytes = ans1OctetString.getOctets();
            ASN1InputStream octetAns1InputStream = new ASN1InputStream(octetsBytes);
            ASN1Encodable ans1Encodable = (ASN1Encodable) octetAns1InputStream.readObject();
            AuthorityInformationAccess auth = AuthorityInformationAccess.getInstance(ans1Encodable);
            AccessDescription[] accessDescriptionArray = auth.getAccessDescriptions();
            GeneralName generalName;
            String url;
            for (int index = 0; index < accessDescriptionArray.length; index++) {
                generalName = accessDescriptionArray[index].getAccessLocation();
                url = generalName.toString();
                if (url.length() > 3) {
                    ocspUrls.add(url.substring(3));
                }
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        return ocspUrls;
    }
//    private RevocationResponse isCertificateRevoked(X509Certificate certificate, List<X509Certificate> issuerCertificateList) {
//        // Start out by building the certificate chain
//        // It is done by by retrieving the issuer certificate,
//        // until the issuer is the certificate itself, and that certificate is in
//        // the issuerCertificateList.
//        RevocationResponse revocationResponse = null;
//        int index = 0;
//
//        while (revocationResponse == null && index < issuerCertificateList.size()) {
//            revocationResponse = isCertificateRevoked(certificate, issuerCertificateList.get(index));
//            index++;
//        }
//        return revocationResponse;
//    }

//    private RevocationResponse isCertificateRevoked(X509Certificate certificate, X509Certificate issuerCertificate) {
//        RevocationResponse revocationResponse = null;
//
//        try {
//            OCSPReqGenerator ocspReqGenerator = new OCSPReqGenerator();
//
//            // ToDo : the middle certificate might be downloaded from ldap...
//            CertificateID certificateID = new CertificateID(CertificateID.HASH_SHA1, issuerCertificate, certificate.getSerialNumber());
//            ocspReqGenerator.addRequest(certificateID);
//            CertificateUtil certificateUtil = new CertificateUtil();
//            String url;
//            List<URL> urlList = certificateUtil.getOcspUrls(certificate);
//            if (urlList.size() <= 0) {
//                url = this.GetServerUriFromCertificate(certificate);
//            } else {
//                url = urlList.get(0).toString();
//            }
//
//            byte[] postByte = HttpClient.doPostOCSPRequest(ocspReqGenerator.generate().getEncoded(), url);
//            OCSPResp resp = new OCSPResp(postByte);
//
//            revocationResponse = this.validateOCSPResponse(certificateID, resp);
//        } catch (Exception e) {
//            log.error("Error: " + e.getMessage(), e);
//        }
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
        /*   catch (IllegalStateException exception){

  }
  catch (InternalException e1) {
      e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
  }
  catch (IOException e1) {
      e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
  }
  catch (RevocationException e1) {
      e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
  }
  catch (MalformedURLException e1) {
      e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
  }
  catch (OCSPException e1) {
      e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
  }
  catch (Exception e1) {
      e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
  }

  if (resp.getStatus() != OCSPResponseStatus.SUCCESSFUL) {
          throw new IllegalStateException("ocsp response status: " + resp.getStatus());
      }

      Object responseObject = resp.getResponseObject();
      if (!(responseObject instanceof BasicOCSPResp)) {
          throw new IllegalStateException("ocsp response is of unexcepted type");
      }

      BasicOCSPResp basicResp = (BasicOCSPResp) responseObject;
        */
        /* check condition:
            The certificate identified in a received response corresponds to
            that which was identified in the corresponding request;
        */
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
        /*    SingleResp[] responses = basicResp.getResponses();
            if (responses.length != 1) {
                throw new IllegalStateException("unexpected number of responses received");
            }

            if (!id.getSerialNumber().equals(responses[0].getCertID().getSerialNumber())) {
                throw new Exception("Serial number mismatch problem");
            }

            X509Certificate ocspCertificate = findOcspClientCertificate(basicResp.getCerts("BC"));
              */
        /* check condition
           The signature on the response is valid;
        */
        /* if(!verifyOcspCertificateChain(certificate, ocspCertificate)) {
         throw new IllegalStateException("Certificate used to sign OCSP Response could not be verified");
     }   */
        /* check the signature on the ocsp response */
        /* if (!basicResp.verify(ocspCertificate.getPublicKey(), "BC")) {
     throw new Exception("signature validation failed for ocsp response");
 }
 if (!canSignOcspResponses(ocspCertificate)) {
     throw new Exception("ocsp signing certificate has not been cleared for ocsp response signing");
 }
 /* check expiry of the signing certificate */
        /*if (!certificateValid(ocspCertificate)) {
          throw new IllegalStateException("ocsp signing certificate is not valid");
      }  */
        /* check condition
           The time at which the status being indicated is known to be
           correct (thisUpdate) is sufficiently recent.
        */
        /*  SingleResp response = responses[0];
    long diff = response.getThisUpdate().getTime() - new Date().getTime();
    if (diff > 60 * 1000L) {
        throw new Exception("ocsp response signature is from the future. Timestamp of thisUpdate field: " + new Date(response.getThisUpdate().getTime()));
    }
    if (response.getNextUpdate() != null && response.getNextUpdate().before(new Date())) {
        throw new Exception("ocsp response is no longer valid");
    }
    Object certStatus = response.getCertStatus();
    if (certStatus == null) {
        return false;
    } else if (certStatus instanceof RevokedStatus) {
        return true;
    } else if (certStatus instanceof UnknownStatus) {
        throw new Exception("ocsp response indicates unknown certificate status");
    } else {
        throw new Exception("unknown status");
    }
} catch (Exception e) {
    throw new RuntimeException(e);
}        */