Skip to content
OcspLookup.java 30.3 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.
 * <p/>
 * http://www.nets.eu/dk-da/Service/kundeservice/nemid-tu/NemID-tjenesteudbyderpakken-okt-2014/Documents/Specifikationsdokument%20for%20OCSP.pdf
 * <p/>
 * TODO DLK: A lot of code in this class uses deprecated API. We need to review it, usually it is a bad sign.
dlk-truelink's avatar
dlk-truelink committed
 */
@SuppressWarnings("deprecation")
public class OcspLookup implements IRevocationLookup {
    private final Log log = LogFactory.getLog(OcspLookup.class);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
    /**
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     */
    private final HashMap<String, X509Certificate> rootCertificateMap;
    /**
     * The ocsp cache
     */
    // Set cache time to one hour
    private final ICache<X500Principal, RevocationResponse> ocspCache = CacheFactory.getInstance().getOcspLookupCache();
    private OcspConfig configuration;
    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());
        }

        rootCertificateMap = new HashMap<>();
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;

        rootCertificateMap = new HashMap<>();
        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<>();
        //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;
        rootCertificateMap = new HashMap<>();
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 the certificate status on a OCSP server.
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     *
     * @param certificate The certificate to check.
     * @return The RevocationResponse object that contains the result.
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
     */
    public RevocationResponse checkCertificate(X509Certificate certificate) throws RevocationException {
        OutVariable<RevocationResponse> value = new OutVariable<>();
        RevocationResponse revocationResponse;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        if (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)) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // the cached value is to old, get new value
                revocationResponse = revocationResponse(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 = revocationResponse(certificate);
            /*if(revocationResponse != null)
            {
                // put revocationResponse in cache
                ocspCache.set(certificate.getSubjectX500Principal(), revocationResponse);
            } */
        }

        return revocationResponse;
    }

    public RevocationResponse checkCertificateAsync(X509Certificate certificate) throws RevocationException {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        // Async not implemented - it is in c#
        return 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<>();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        if (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)) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // the cached value is to old, get new value from online check
                revocationResponse = revocationResponseOnline(certificate);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            revocationResponse = revocationResponseOnline(certificate);
     * Checks the 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 {
        // The response was not in the cache - we must validate the certificate online.
        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;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        // Is the certificate that we are about to verify, the root certificate itself?
        if (x509Certificate.getIssuerDN().getName().equals(issuerX509Certificate.getSubjectX500Principal().getName())) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            throw new RevocationException("Certificate not trusted, as the certificate is self-signed");
            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 (rootCertificateMap.containsKey(issuerX509Certificate.getIssuerDN().getName())) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    // 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.

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    List<String> issuerUrlList = getAuthorityInformationAccessOcspUrl(issuerX509Certificate);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    RevocationResponse issuerRevocationResponse;

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    if (issuerUrlList.size() > 0) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                        // 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
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                    if (issuerRevocationResponse == null) {
                        revocationResponse.setIsValid(false);
                        //revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("The issuing certificate could not be validated.");
                    } else {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                        // the issuer certificate is validated, the validity of the issuer certificate
                        // is copied to the revocationResponse
                        revocationResponse.setIsValid(issuerRevocationResponse.isValid());
                    }

                    // 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.
     */
    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("Certificate to verify is null");
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        }
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.isEmpty()) {
                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()) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            log.debug("Online OCSP call for [" + serverX509Certificate.getSubjectDN() + "] to url=" + url);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        }
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
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            byte[] binaryResp = postData(req, url);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            // 3. Make result object.
            revocationResponse = processOcspResponse(serverX509Certificate, binaryResp);
        } catch (NoSuchProviderException | 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:
                log.warn("OCSPResponse has Status error=" + ocspResponse.getStatus());
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
                // all other then success is.
//                new RevocationException("OCSPResponseStatus not valid!");
    private byte[] postData(OCSPReq req, String url) throws RevocationException {
        try {
            HttpURLConnection conn = postRequest(req.getEncoded(), url);

            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                log.error("HTTP response code from OCSP request to [" + url + "] was: " + conn.getResponseCode());
                throw new RevocationException("HTTP response code from OCSP request to [" + url + "] was: " + conn.getResponseCode());
            }

            int len = conn.getContentLength();

Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            return 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) {
        // 1: A root certificate - exist in root list.
        // 2: A root certificate not trusted/not in list.
        // 3: Issuer certificate, can be downloaded.
        X509Certificate issuerCertificate;

        String key = certificate.getIssuerDN().getName();
        if (rootCertificateMap.containsKey(key)) {
            // root certificate is in the trusted key store (possibility 1)
            issuerCertificate = rootCertificateMap.get(key);
            // 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 = 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
    }

    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
        ASN1InputStream aIn = null;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        try {
            byte[] ext = certificate.getExtensionValue(X509Extensions.AuthorityInfoAccess.getId());
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            aIn = new ASN1InputStream(ext);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            aIn = new ASN1InputStream(((ASN1OctetString) aIn.readObject()).getOctets());
            ASN1Encodable object = aIn.readObject();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            AuthorityInformationAccess auth = AuthorityInformationAccess.getInstance(object);
            AccessDescription[] acc = auth.getAccessDescriptions();
            return acc[0].getAccessLocation().toString().substring(3);
        } catch (IOException e) {
            throw new RevocationException(e);
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        } finally {
            if (aIn != null) {
                try {
                    aIn.close();
                } catch (IOException e) {
                    log.warn("Error closing stream: " + e.getMessage(), e);
                }
            }
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        }
    }
    public List<String> getAuthorityInformationAccessOcspUrl(X509Certificate x509Certificate) {
        List<String> ocspUrls = new ArrayList<>();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        ASN1InputStream extensionAns1InputStream = null;
        ASN1InputStream octetAns1InputStream = null;
        try {
            byte[] extBytes = x509Certificate.getExtensionValue(X509Extensions.AuthorityInfoAccess.getId());
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            extensionAns1InputStream = new ASN1InputStream(extBytes);
            ASN1OctetString ans1OctetString = (ASN1OctetString) extensionAns1InputStream.readObject();
            byte[] octetsBytes = ans1OctetString.getOctets();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            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
        } finally {
            if (extensionAns1InputStream != null) {
                try {
                    extensionAns1InputStream.close();
                } catch (IOException e) {
                    log.warn("Error closing stream: " + e.getMessage(), e);
                }
            }
            if (octetAns1InputStream != null) {
                try {
                    octetAns1InputStream.close();
                } catch (IOException e) {
                    log.warn("Error closing stream: " + 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 | 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;
    }