ee.ria.xroad.proxy.clientproxy.AuthTrustVerifier.java Source code

Java tutorial

Introduction

Here is the source code for ee.ria.xroad.proxy.clientproxy.AuthTrustVerifier.java

Source

/**
 * The MIT License
 * Copyright (c) 2015 Estonian Information System Authority (RIA), Population Register Centre (VRK)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package ee.ria.xroad.proxy.clientproxy;

import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.ArrayUtils;
import org.apache.http.protocol.HttpContext;

import org.bouncycastle.cert.ocsp.OCSPResp;

import ee.ria.xroad.common.CodedException;
import ee.ria.xroad.common.cert.CertChain;
import ee.ria.xroad.common.cert.CertHelper;
import ee.ria.xroad.common.identifier.ClientId;
import ee.ria.xroad.common.identifier.ServiceId;
import ee.ria.xroad.common.util.CertUtils;
import ee.ria.xroad.proxy.conf.KeyConf;

import static ee.ria.xroad.common.ErrorCodes.*;
import static ee.ria.xroad.common.util.CertHashBasedOcspResponderClient.getOcspResponsesFromServer;

/**
 * This class is responsible for verifying the server proxy SSL certificate.
 * Since we need to know the provider name used when making request to the
 * server proxy, we cannot verify the server certificate directly in the
 * AuthTrustManager, because it is impossible to relate the provider name
 * to the current SSL session in the AuthTrustManager.
 *
 * Instead, in the AuthTrustManager, we declare that the server is trusted
 * but then use a RequestInterceptor that will be called after the
 * SSL handshake takes place. We can then retrieve the provider name from
 * the HttpContext (stored there previously by the MultipartSender) and
 * the peer certificates and do the validation of the certificate.
 */
@Slf4j
public final class AuthTrustVerifier {

    public static final String ID_PROVIDERNAME = "request.providerName";

    private AuthTrustVerifier() {
    }

    static void verify(HttpContext context, SSLSession sslSession, URI selectedAddress) {
        log.debug("verify()");

        ServiceId service = (ServiceId) context.getAttribute(ID_PROVIDERNAME);
        if (service == null) {
            throw new CodedException(X_SSL_AUTH_FAILED, "Could not get provider name from context");
        }

        X509Certificate[] certs = getPeerCertificates(sslSession);
        if (certs.length == 0) {
            throw new CodedException(X_SSL_AUTH_FAILED, "Could not get peer certificates from context");
        }

        try {
            verifyAuthCert(service.getClientId(), certs, selectedAddress);
        } catch (Exception e) {
            throw translateException(e);
        }
    }

    private static void verifyAuthCert(ClientId serviceProvider, X509Certificate[] certs, URI address)
            throws Exception {
        CertChain chain;
        List<OCSPResp> ocspResponses;
        try {
            List<X509Certificate> additionalCerts = Arrays
                    .asList((X509Certificate[]) ArrayUtils.subarray(certs, 1, certs.length));
            chain = CertChain.create(serviceProvider.getXRoadInstance(), certs[0], additionalCerts);
            ocspResponses = getOcspResponses(chain.getAllCertsWithoutTrustedRoot(), address.getHost());
        } catch (CodedException e) {
            throw e.withPrefix(X_SSL_AUTH_FAILED);
        }

        CertHelper.verifyAuthCert(chain, ocspResponses, serviceProvider);
    }

    /**
     * Gets OCSP responses for each certificate in the chain. If the OCSP
     * response is not locally available (cached), it will be retrieved
     * from the internal OCSP responder that is located at the given address.
     */
    private static List<OCSPResp> getOcspResponses(List<X509Certificate> chain, String address) throws Exception {
        List<X509Certificate> certs = new ArrayList<>();
        List<OCSPResp> responses = new ArrayList<>();

        // Check for locally available OCSP responses
        for (X509Certificate cert : chain) {
            OCSPResp response = null;
            try {
                // Do we have a cached OCSP response for that cert?
                log.trace("get ocsp response from key conf");
                response = KeyConf.getOcspResponse(cert);
                log.trace("ocsp response from key conf: {}", response);
            } catch (CodedException e) {
                // Log it and continue; only thrown if the response could
                // not be loaded from a file -- not important to us here.
                log.warn("Cached OCSP response could not be found", e);
            }

            if (response != null) {
                responses.add(response);
            } else {
                // Did not find response locally, add the hash to be retrieved
                // from server.
                certs.add(cert);
            }
        }

        // Retrieve OCSP responses for those certs whose responses
        // are not locally available, from ServerProxy
        if (!certs.isEmpty()) {
            log.trace("number of certs that still need ocsp responses: {}", certs.size());
            responses.addAll(getAndCacheOcspResponses(certs, address));
        } else {
            log.trace("all the certs have ocsp responses");
        }

        return responses;
    }

    /**
     * Sends the GET request with all cert hashes which need OCSP responses.
     */
    private static List<OCSPResp> getAndCacheOcspResponses(List<X509Certificate> hashes, String address)
            throws Exception {
        List<OCSPResp> receivedResponses;
        try {
            log.trace("get ocsp responses from server {}", address);
            receivedResponses = getOcspResponsesFromServer(address, CertUtils.getCertHashes(hashes));
        } catch (Exception e) {
            throw new CodedException(X_INTERNAL_ERROR, e);
        }

        // Did we get OCSP response for each cert hash?
        if (receivedResponses.size() != hashes.size()) {
            throw new CodedException(X_INTERNAL_ERROR,
                    "Could not get all OCSP responses from server " + "(expected %s, but got %s)", hashes.size(),
                    receivedResponses.size());
        }

        // Cache the responses locally
        log.trace("got ocsp responses, setting them to key conf");
        KeyConf.setOcspResponses(hashes, receivedResponses);

        return receivedResponses;
    }

    private static X509Certificate[] getPeerCertificates(SSLSession session) {
        if (session == null) {
            throw new CodedException(X_SSL_AUTH_FAILED, "No TLS session");
        }

        try {
            // Note: assuming X509-based auth
            return (X509Certificate[]) session.getPeerCertificates();
        } catch (SSLPeerUnverifiedException e) {
            log.error("Error while getting peer certificates", e);
            throw new CodedException(X_SSL_AUTH_FAILED,
                    "Service provider " + "did not send correct authentication certificate");
        }
    }

}