Skip to content
OcspLookup.java 29.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;
dlk-truelink's avatar
dlk-truelink committed
import java.io.IOException;
import java.net.MalformedURLException;
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.Calendar;
import java.util.Date;
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;
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.X509Extensions;
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
import org.bouncycastle.cert.ocsp.RevokedStatus;
import org.bouncycastle.ocsp.*;
dlk-truelink's avatar
dlk-truelink committed
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;
/**
 * 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
    /**
     * 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 final ICache<X500Principal, RevocationResponse> ocspCache = CacheFactory.getInstance().getOcspLookupCache();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed

Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
    /**
     * The LDAP lookup client
     */
dlk-truelink's avatar
dlk-truelink committed
//    private ICertificateLookup certificateLookup;
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 {
            configuration = ConfigurationHandler.getInstance().getOcspConfig();
        } catch (URISyntaxException e) {
            throw new ConfigurationException(e.getMessage());
        }

dlk-truelink's avatar
dlk-truelink committed
//        this.certificateLookup = LdapLookupFactory.createLdapLookupClient();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    }

    /**
     * Instantiates OcspLookup and loads the OCES default root certificate.
     *
     * @param conf Configuration parameters
     */
    public OcspLookup(OcspConfig conf) {
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
        this.configuration = conf;
        this.rootcertList = null;
dlk-truelink's avatar
dlk-truelink committed
//        this.certificateLookup = LdapLookupFactory.createLdapLookupClient();
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.rootcertList = new ArrayList<X509Certificate>();
        this.rootcertList.add(defaultRootCertificate);
dlk-truelink's avatar
dlk-truelink committed
//        this.certificateLookup = LdapLookupFactory.createLdapLookupClient();
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;
        this.rootcertList = defaultRootCertificateList;
dlk-truelink's avatar
dlk-truelink committed
//        this.certificateLookup = LdapLookupFactory.createLdapLookupClient();
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
    }
    /**
     * 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
                // 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);
                }
            }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        }
        else
        {
            // respons is not in cache
            revocationResponse = this.checkCertificateOnline(certificate);
            if(revocationResponse != null)
            {
                // put revocationResponse in cache
                ocspCache.set(certificate.getSubjectX500Principal(), revocationResponse);
            }
        }

        return revocationResponse;
    }
    public RevocationResponse checkCertificateOnline(X509Certificate certificate) throws RevocationException {
        // The response was not in the cache - we must validete the certificate our self
        RevocationResponse response = null;
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
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
                // hack
                //rootcertList = null;

               throw new RevocationException("No root certificate has been defined.");
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed


           /* if (configuration.getServerUrl() == null) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // Use OCSP server specified in certificate
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
                //serverUrl = GetServerUriFromCertificate(certificate);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            } 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.");
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
            }     */

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


        /* 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();

            */

       /*

        CertificateID

        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 = 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 + "]");
                    }
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);
        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.");
        }

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;
    }       */

dlk-truelink's avatar
dlk-truelink committed
	public String GetServerUriFromCertificate(X509Certificate certificate) throws RevocationException {
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);
        }
    }
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed

    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;
    }

dlk-truelink's avatar
dlk-truelink committed
	private RevocationResponse isCertificateRevoked(X509Certificate certificate, X509Certificate issuerCertificate) {
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
        RevocationResponse revocationResponse = null;

        try {

            //OCSPReqBuilder builder = new OCSPReqBuilder();
            //OCSPRespBuilder OCSPRespBuilder = new OCSPRespBuilder();
            OCSPReqGenerator ocspReqGenerator = new OCSPReqGenerator();

            // ToDo : the middle certificate migth 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)
            {
                // throw exception
                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 (MalformedURLException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        catch (NoSuchProviderException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        catch (RevocationException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        catch (OCSPException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        /*   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;
             */
        /*    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);
        }        */

        return revocationResponse;
    }

dlk-truelink's avatar
dlk-truelink committed
	private RevocationResponse validateOCSPResponse(CertificateID certificateID, OCSPResp resp) throws OCSPException, NoSuchProviderException
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
    {
        RevocationResponse revocationResponse = null;

        if(resp == null){
            return revocationResponse;
        }

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


        Object responseObject = resp.getResponseObject();
        if (!(responseObject instanceof BasicOCSPResp)) {
            return revocationResponse; //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;
         */
        SingleResp[] responses = basicResp.getResponses();
        if (responses.length != 1) {
            return revocationResponse; //throw new IllegalStateException("unexpected number of responses received");
        }

        if (!certificateID.getSerialNumber().equals(responses[0].getCertID().getSerialNumber())) {
            return revocationResponse; //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)) {
            return revocationResponse; //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")) {
           return revocationResponse; //throw new Exception("signature validation failed for ocsp response");
        }

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



        /* check expiry of the signing certificate */
        if (!certificateValid(ocspCertificate)) {
            return revocationResponse; //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)
        {
            // next update null - can not be used
            // or can it ???
           return revocationResponse;
        }

        if (response.getNextUpdate() != null && response.getNextUpdate().before(new Date())) {
            return revocationResponse; //throw new Exception("ocsp response is no longer valid");
        }

        Object certStatus = response.getCertStatus();

        if (certStatus == null) {

dlk-truelink's avatar
dlk-truelink committed
        	/*
        	 * new Date(2013, 1, 1) in java is year 1900 + 2013 = 3913, 1 of February.
        	 * 
        	 * So if we want to set it to 1.1.2013, let's use Calendar object like this:
        	 * 
        	 * Calendar c = Calendar.getInstance();
        	 * 
        	 * c.set(2013, 0, 1);
        	 */
//            return  new RevocationResponse(true, new Date(2013,1,1) );
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
            // hack:
dlk-truelink's avatar
dlk-truelink committed
			/*
			 * TODO: Find why certStatus can be null, for now we just consider
			 * certificate as non-revoked and set expiration date to current
			 * date plus one day to avoid hardcoding of any exact date
			 */
        	Calendar c = Calendar.getInstance();
        	c.add(Calendar.DATE, 1);
        	return new RevocationResponse(true, c.getTime());
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
            //return revocationResponse;
        } else if (certStatus instanceof RevokedStatus){
            return new RevocationResponse(true , response.getNextUpdate());
        } else if (certStatus instanceof UnknownStatus) {
            return revocationResponse; // throw new Exception("ocsp response indicates unknown certificate status");
        } else {
            return revocationResponse; //throw new Exception("unknown status");
        }
    }

    private boolean certificateValid(X509Certificate ocspCertificate) {
		try {
			ocspCertificate.checkValidity();
			return true;
		} catch (CertificateExpiredException e) {
			return false;
		} catch (CertificateNotYetValidException e) {
			return false;
		}
	}

	private boolean canSignOcspResponses(X509Certificate ocspCertificate) {
		try {
			return ocspCertificate.getExtendedKeyUsage().contains(KeyPurposeId.id_kp_OCSPSigning.getId());
		} catch (CertificateParsingException e) {
			throw new RuntimeException("ocsp signing certificate has not been cleared for ocsp response signing");
		}
	}

	/*private boolean verifyOcspCertificateChain(OcesCertificateFacade certificate, X509Certificate ocspCertificate) {
		CA parent = certificate.getSigningCA();
		return ChainVerifier.verifyTrust(ocspCertificate, parent);
	}*/

	private X509Certificate findOcspClientCertificate(X509Certificate[] certs) {
		int KeyUsageDigitalSignature = 0;
		X509Certificate ocspCert = null;
		for (int i = 0; i < certs.length; i++) {
			X509Certificate certificate = certs[i];
			boolean[] keyUsage = certificate.getKeyUsage();
			if (keyUsage != null && keyUsage[KeyUsageDigitalSignature] && null != certificate.getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck.getId())) {
				ocspCert = certificate;
			}
		}
		if(ocspCert == null) { throw new RuntimeException("Could not find valid OCSP certificate"); }
		return ocspCert;
	}
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
}