Skip to content
OcspLookup.java 31.2 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;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær 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;
import dk.gov.oiosi.security.revocation.crl.CrlLookup;
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 javax.security.auth.x500.X500Principal;
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;

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

    /**
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær 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) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                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 recursive, so check the cache first
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        RevocationResponse revocationResponse;
        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);
            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");
        }

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        X509Certificate issuerX509Certificate = findIssuerCertificate(x509Certificate);
        if (issuerX509Certificate == null) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            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;
        // Is the the certificate to verify the root certificate itself?
        if (x509Certificate.getIssuerDN().getName().equals(issuerX509Certificate.getSubjectX500Principal().getName())) {
            throw new RevocationException("Certificate not trusted, as the certificate is self signed");
        } else {
            revocationResponse = revocationResponseOnline(x509Certificate, issuerX509Certificate);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            if (revocationResponse != null && 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
                    ocspCache.add(x509Certificate.getSubjectX500Principal(), revocationResponse);
                } else {
                    // we do not yet know if the certificate is valid.
                    // the certificate might be good, but if the issuing 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 intermediate certificate (the issuer certificate).
                    // according 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);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                        // we need to validate with crl instead
                        // It does not contain the Authority Info Access, containing the rl to where the certificate must be validated
                        // We must therefore guess, that the certificate is valid.
                        CrlLookup crlLookupClient = new CrlLookup();
                        issuerRevocationResponse = crlLookupClient.checkCertificate(issuerX509Certificate);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed

                    // 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());
                    } else {
                        revocationResponse.setIsValid(false);
                        //revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("The issuing certificate could not be validated.");
                    }

                    // update the cache
                    this.ocspCache.add(x509Certificate.getSubjectX500Principal(), revocationResponse);
                }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // the certificate is Not valid
                // no need to check the issuer certificate
                this.ocspCache.add(x509Certificate.getSubjectX500Principal(), revocationResponse);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            }
    /**
     * @param x509Certificate       The certificate to verify.
     * @param issuerX509Certificate Issuer certificate of x509Certificate.
     * @return RevocationResponse
     * @throws RevocationException On error...
     */
    private RevocationResponse revocationResponseOnline(X509Certificate x509Certificate, X509Certificate issuerX509Certificate) throws RevocationException {
        if (x509Certificate == null) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            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 = getAuthorityInformationAccessOcspUrl(x509Certificate);
            if (urlList.size() == 0) {
                throw new RevocationException("No OCSP url found in certificate: " + x509Certificate.getSubjectDN().getName());
            // we always validate against the first defined url
            String url = urlList.get(0);
            revocationResponse = revocationResponseOnline(x509Certificate, issuerX509Certificate, url);
        } catch (OCSPException e) {
            log.warn(e.getMessage(), e);
            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 (log.isDebugEnabled()) {
            log.debug("Make OCSP call for " + serverX509Certificate.getSubjectDN());
        }
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 = generateOcspRequest(issuerX509Certificate, serverX509Certificate.getSerialNumber());

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

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            // 3. Make result object.
            revocationResponse = 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);
        }

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

        return ocspRequestGenerator.generate();
    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());
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                } else if (certStatus.equals(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) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            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) {
        // 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;

        String key = certificate.getIssuerDN().getName();
        if (this.rootCertificateMap.containsKey(key)) {
            // root certificate is in the trusted key store (possibility 1)
            issuerCertificate = this.rootCertificateMap.get(key);
            // So down to possibility 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 = octetAns1InputStream.readObject();
            AuthorityInformationAccess auth = AuthorityInformationAccess.getInstance(ans1Encodable);
            AccessDescription[] accessDescriptionArray = auth.getAccessDescriptions();
            GeneralName generalName;
            String url;
            for (AccessDescription anAccessDescriptionArray : accessDescriptionArray) {
                generalName = anAccessDescriptionArray.getAccessLocation();
                url = generalName.toString();
                if (url.length() > 3) {
                    ocspUrls.add(url.substring(3));
                }
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed

Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed

    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 X509Certificate findOcspClientCertificate(X509Certificate[] certs) {
        int KeyUsageDigitalSignature = 0;
        X509Certificate ocspCert = null;
        for (X509Certificate certificate : certs) {
            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
    /**
     * @return Always returns OCSP
     * @since OIORASP 1.3.0
     */
    @Override
    public RevocationSourceType getRevocationSourceType() {
        return RevocationSourceType.OCSP;
    }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
}