org.dcache.http.HttpsTransferService.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.http.HttpsTransferService.java

Source

/* dCache - http://www.dcache.org/
 *
 * Copyright (C) 2017-2018 Deutsches Elektronen-Synchrotron
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.dcache.http;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.InetAddresses;

import diskCacheV111.util.CacheException;
import diskCacheV111.vehicles.HttpProtocolInfo;

import dmg.cells.nucleus.CDC;

import eu.emi.security.authn.x509.CrlCheckingMode;
import eu.emi.security.authn.x509.OCSPCheckingMode;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.SslHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.util.UUID;

public class HttpsTransferService extends HttpTransferService {
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpsTransferService.class);

    private static final String PROTOCOL_HTTPS = "https";

    private Path _serverCertificatePath;
    private Path _serverKeyPath;
    private Path _serverCaPath;
    private CrlCheckingMode _crlCheckingMode;
    private OCSPCheckingMode _ocspCheckingMode;
    private volatile SSLEngine _sslEngine;

    @Required
    public void setServerCertificatePath(Path serverCertificatePath) {
        _serverCertificatePath = serverCertificatePath;
    }

    @Required
    public void setServerKeyPath(Path serverKeyPath) {
        _serverKeyPath = serverKeyPath;
    }

    @Required
    public void setServerCaPath(Path serverCaPath) {
        _serverCaPath = serverCaPath;
    }

    @Required
    public void setCrlCheckingMode(CrlCheckingMode crlCheckingMode) {
        _crlCheckingMode = crlCheckingMode;
    }

    @Required
    public void setOcspCheckingMode(OCSPCheckingMode ocspCheckingMode) {
        _ocspCheckingMode = ocspCheckingMode;
    }

    /**
     * Obtain the hostname or IP address from a URL.  Unlike URI#getHost, this
     * method returns any IPv6 address without the square brackets.
     * @param url
     * @return
     */
    @VisibleForTesting
    static String getHost(URI url) {
        String host = url.getHost();
        return host != null && !host.isEmpty() && host.charAt(0) == '[' && host.charAt(host.length() - 1) == ']'
                ? host.substring(1, host.length() - 1)
                : host;
    }

    @Override
    protected URI getUri(HttpProtocolInfo protocolInfo, int port, UUID uuid)
            throws SocketException, CacheException, URISyntaxException {

        URI plainUrl = super.getUri(protocolInfo, port, uuid);
        String host = getHost(plainUrl);
        try {
            if (InetAddresses.isInetAddress(host)) {
                // An IP address is unlikely to be in the X.509 host credential.
                host = InetAddress.getByName(host).getCanonicalHostName();
            }
            if (InetAddresses.isInetAddress(host)) {
                LOGGER.warn("Unable to resolve IP address {} to a canonical name", host);
            }
        } catch (UnknownHostException e) {
            // This should not happen as getByName should never throw this
            // exception for a valid IP address
            LOGGER.warn("Unable to resolve IP address {}: {}", host, e.toString());
        }
        return new URI(PROTOCOL_HTTPS, plainUrl.getUserInfo(), host, plainUrl.getPort(), plainUrl.getPath(),
                plainUrl.getQuery(), plainUrl.getFragment());
    }

    @Override
    protected synchronized void startServer() throws IOException {
        super.startServer();
        try {
            _sslEngine = createContext().createSSLEngine();
            _sslEngine.setUseClientMode(false);
            _sslEngine.setWantClientAuth(false);
        } catch (Exception e) {
            throw new IOException("Failed to create SSL engine: " + e, e);
        }

    }

    @Override
    protected void addChannelHandlers(ChannelPipeline pipeline) {
        pipeline.addLast("ssl", new SslHandler(_sslEngine));
        super.addChannelHandlers(pipeline);
    }

    private synchronized SSLContext createContext() throws Exception {

        return org.dcache.ssl.CanlContextFactory.custom().withCertificateAuthorityPath(_serverCaPath)
                .withCrlCheckingMode(_crlCheckingMode).withOcspCheckingMode(_ocspCheckingMode)
                .withCertificatePath(_serverCertificatePath).withKeyPath(_serverKeyPath).withLazy(false)
                .withLoggingContext(new CDC()::restore).buildWithCaching().call();
    }
}