All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openehealth.ipf.commons.audit.CustomTlsParameters Maven / Gradle / Ivy

/*
 * Copyright 2020 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.openehealth.ipf.commons.audit;

import io.vertx.core.net.JksOptions;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.net.PfxOptions;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol.HTTPS_CIPHERSUITES;
import static org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol.JAVAX_NET_SSL_KEYSTORE;
import static org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol.JAVAX_NET_SSL_KEYSTORE_PASSWORD;
import static org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol.JAVAX_NET_SSL_KEYSTORE_TYPE;
import static org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol.JAVAX_NET_SSL_TRUSTSTORE;
import static org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol.JAVAX_NET_SSL_TRUSTSTORE_PASSWORD;
import static org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol.JDK_TLS_CLIENT_PROTOCOLS;

/**
 * {@link TlsParameters} that can be set independently of the javax.net.ssl system
 * properties. Still, a newly instantiated instance of this class defaults to these
 * properties.
 */
public class CustomTlsParameters implements VertxTlsParameters {

    private String provider = "SunJSSE";
    private String tlsProtocol = "TLSv1.2";
    private String certificateType = "SunX509";

    private String certAlias;

    private String keyStoreType;
    private String trustStoreType;
    private String keyStoreFile;
    private String keyStorePassword;
    private String trustStoreFile;
    private String trustStorePassword;

    private String enabledCipherSuites;
    private String enabledProtocols;

    private int sessionTimeout;
    private boolean performDomainValidation;
    private final List sniHostnames = new ArrayList<>();


    public void setProvider(String provider) {
        this.provider = provider;
    }

    public void setTlsProtocol(String tlsProtocol) {
        this.tlsProtocol = tlsProtocol;
    }

    public void setKeyStoreType(String keyStoreType) {
        this.keyStoreType = keyStoreType;
    }

    public void setTrustStoreType(String trustStoreType) {
        this.trustStoreType = trustStoreType;
    }

    public void setCertificateType(String certificateType) {
        this.certificateType = certificateType;
    }

    public void setCertAlias(String certAlias) {
        this.certAlias = certAlias;
    }

    public void setKeyStoreFile(String keyStoreFile) {
        this.keyStoreFile = keyStoreFile;
    }

    public void setKeyStorePassword(String keyStorePassword) {
        this.keyStorePassword = keyStorePassword;
    }

    public void setTrustStoreFile(String trustStoreFile) {
        this.trustStoreFile = trustStoreFile;
    }

    public void setTrustStorePassword(String trustStorePassword) {
        this.trustStorePassword = trustStorePassword;
    }

    public void setEnabledCipherSuites(String enabledCipherSuites) {
        this.enabledCipherSuites = enabledCipherSuites;
    }

    public void setEnabledProtocols(String enabledProtocols) {
        this.enabledProtocols = enabledProtocols;
    }

    public void setSessionTimeout(int sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public void setPerformDomainValidation(boolean performDomainValidation) {
        this.performDomainValidation = performDomainValidation;
    }

    public List getSniHostnames() {
        return sniHostnames;
    }

    public CustomTlsParameters() {
        keyStoreType = System.getProperty(JAVAX_NET_SSL_KEYSTORE_TYPE, KeyStore.getDefaultType());
        trustStoreType = keyStoreType;
        keyStoreFile = System.getProperty(JAVAX_NET_SSL_KEYSTORE);
        keyStorePassword = System.getProperty(JAVAX_NET_SSL_KEYSTORE_PASSWORD);
        trustStoreFile = System.getProperty(JAVAX_NET_SSL_TRUSTSTORE);
        trustStorePassword = System.getProperty(JAVAX_NET_SSL_TRUSTSTORE_PASSWORD);
        enabledCipherSuites = System.getProperty(HTTPS_CIPHERSUITES);
        enabledProtocols = System.getProperty(JDK_TLS_CLIENT_PROTOCOLS, "TLSv1.2");
    }

    private Function sslSocketFactoryConfigurer() {
        return sslSocketFactory -> new SSLSocketFactoryDecorator(sslSocketFactory, sslSocketConfigurer());
    }

    private Function sslSocketConfigurer() {
        return sslSocket -> {
            if (enabledCipherSuites != null) {
                sslSocket.setEnabledCipherSuites(split(enabledCipherSuites));
            }
            if (enabledProtocols != null) {
                sslSocket.setEnabledProtocols(split(enabledProtocols));
            }
            if (sniHostnames.isEmpty()) {
                SSLParameters sslParameters = sslSocket.getSSLParameters();
                sslParameters.setServerNames(sniHostnames.stream()
                        .map(SNIHostName::new)
                        .collect(Collectors.toList()));
                sslSocket.setSSLParameters(sslParameters);
            }
            if (performDomainValidation) {
                SSLParameters sslParameters = sslSocket.getSSLParameters();
                sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
                sslSocket.setSSLParameters(sslParameters);
            }
            return sslSocket;
        };
    }

    private Function sslEngineConfigurer() {
        return sslEngine -> {
            if (enabledCipherSuites != null) {
                sslEngine.setEnabledCipherSuites(split(enabledCipherSuites));
            }
            if (enabledProtocols != null) {
                sslEngine.setEnabledProtocols(split(enabledProtocols));
            }
            return sslEngine;
        };
    }

    private String[] split(String s) {
        return s.split("\\s*,\\s*");
    }

    @Override
    public SSLContext getSSLContext() {
        try {
            KeyStore keyStore = getKeyStore(keyStoreType, keyStoreFile, keyStorePassword);
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(certificateType, provider);
            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
            KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

            if (keyManagers != null && certAlias != null) {
                for (int i = 0; i < keyManagers.length; i++) {
                    if (keyManagers[i] instanceof X509KeyManager) {
                        keyManagers[i] = new AliasX509ExtendedKeyManager((X509KeyManager) keyManagers[i], certAlias);
                    }
                }
            }

            KeyStore trustStore = getKeyStore(trustStoreType, trustStoreFile, trustStorePassword);
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(certificateType, provider);
            trustManagerFactory.init(trustStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

            SecureRandom secureRandom = new SecureRandom();
            SSLContext sslContext = SSLContext.getInstance(tlsProtocol, provider);
            sslContext.init(keyManagers, trustManagers, secureRandom);

            if (sessionTimeout > 0) {
                sslContext.getClientSessionContext().setSessionTimeout(sessionTimeout);
            }

            return new CustomSSLContext(new SSLContextSpiDecorator(
                    sslContext,
                    sslEngineConfigurer(),
                    sslSocketFactoryConfigurer()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void initNetClientOptions(NetClientOptions options) {
        if ("JKS".equalsIgnoreCase(keyStoreType)) {
            options.setKeyStoreOptions(new JksOptions()
                    .setPath(keyStoreFile)
                    .setPassword(keyStorePassword));
        } else {
            options.setPfxKeyCertOptions(new PfxOptions()
                    .setPath(keyStoreFile)
                    .setPassword(keyStorePassword));
        }
        if ("JKS".equalsIgnoreCase(trustStoreType)) {
            options.setTrustStoreOptions(new JksOptions()
                    .setPath(trustStoreFile)
                    .setPassword(trustStorePassword));
        } else {
            options.setPfxTrustOptions(new PfxOptions()
                    .setPath(trustStoreFile)
                    .setPassword(trustStorePassword));
        }
        Stream.of(split(enabledProtocols))
                .forEach(options::addEnabledSecureTransportProtocol);

        if (enabledCipherSuites != null) {
            Stream.of(split(enabledCipherSuites))
                    .forEach(options::addEnabledCipherSuite);
        }
    }

    private KeyStore getKeyStore(String type, String storePath, String password) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(type);
        try (InputStream in = Files.newInputStream(Paths.get(storePath))) {
            keyStore.load(in, password.toCharArray());
            return keyStore;
        }
    }


    private static final class CustomSSLContext extends SSLContext {

        CustomSSLContext(SSLContextSpiDecorator sslContextSpiDecorator) {
            super(sslContextSpiDecorator,
                    sslContextSpiDecorator.getDelegate().getProvider(),
                    sslContextSpiDecorator.getDelegate().getProtocol());
        }

    }

    private static final class SSLContextSpiDecorator extends SSLContextSpi {

        private final SSLContext sslContext;
        private final Function sslEngineConfigurer;
        private final Function sslSocketFactoryConfigurer;

        public SSLContextSpiDecorator(SSLContext sslContext,
                                      Function sslEngineConfigurer,
                                      Function sslSocketFactoryConfigurer) {
            this.sslContext = sslContext;
            this.sslEngineConfigurer = sslEngineConfigurer;
            this.sslSocketFactoryConfigurer = sslSocketFactoryConfigurer;
        }

        SSLContext getDelegate() {
            return sslContext;
        }

        @Override
        protected void engineInit(KeyManager[] keyManagers,
                                  TrustManager[] trustManagers,
                                  SecureRandom secureRandom) throws KeyManagementException {
            sslContext.init(keyManagers, trustManagers, secureRandom);
        }

        @Override
        protected SSLSocketFactory engineGetSocketFactory() {
            SSLSocketFactory factory = sslContext.getSocketFactory();
            return sslSocketFactoryConfigurer.apply(factory);
        }

        @Override
        protected SSLServerSocketFactory engineGetServerSocketFactory() {
            throw new UnsupportedOperationException();
        }

        @Override
        protected SSLEngine engineCreateSSLEngine() {
            SSLEngine engine = sslContext.createSSLEngine();
            return sslEngineConfigurer.apply(engine);
        }

        @Override
        protected SSLEngine engineCreateSSLEngine(String host, int port) {
            SSLEngine engine = sslContext.createSSLEngine(host, port);
            return sslEngineConfigurer.apply(engine);
        }

        @Override
        protected SSLSessionContext engineGetServerSessionContext() {
            return sslContext.getServerSessionContext();
        }

        @Override
        protected SSLSessionContext engineGetClientSessionContext() {
            return sslContext.getClientSessionContext();
        }
    }


    private static final class SSLSocketFactoryDecorator extends SSLSocketFactory {

        private final SSLSocketFactory delegate;
        private final Function sslSocketConfigurer;

        public SSLSocketFactoryDecorator(SSLSocketFactory delegate, Function sslSocketConfigurer) {
            this.delegate = delegate;
            this.sslSocketConfigurer = sslSocketConfigurer;
        }

        @Override
        public String[] getDefaultCipherSuites() {
            return delegate.getDefaultCipherSuites();
        }

        @Override
        public String[] getSupportedCipherSuites() {
            return delegate.getSupportedCipherSuites();
        }

        @Override
        public Socket createSocket() throws IOException {
            return configureSocket(delegate.createSocket());
        }

        @Override
        public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
            return configureSocket(delegate.createSocket(s, host, port, autoClose));
        }

        @Override
        public Socket createSocket(String host, int port) throws IOException {
            return configureSocket(delegate.createSocket(host, port));
        }

        @Override
        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
            return configureSocket(delegate.createSocket(host, port, localHost, localPort));
        }

        @Override
        public Socket createSocket(InetAddress host, int port) throws IOException {
            return configureSocket(delegate.createSocket(host, port));
        }

        @Override
        public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
            return configureSocket(delegate.createSocket(address, port, localAddress, localPort));
        }

        public SSLSocketFactory getDelegate() {
            return this.delegate;
        }

        private Socket configureSocket(Socket s) {
            SSLSocket socket = (SSLSocket) s;
            return sslSocketConfigurer.apply(socket);
        }
    }


    private static final class AliasX509ExtendedKeyManager extends X509ExtendedKeyManager {
        private final String certAlias;
        private final X509KeyManager keyManager;

        public AliasX509ExtendedKeyManager(X509KeyManager keyManager, String certAlias) {
            this.keyManager = keyManager;
            this.certAlias = certAlias;
        }

        public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
            return certAlias != null ? certAlias : keyManager.chooseClientAlias(keyTypes, issuers, socket);
        }

        public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
            return certAlias != null ? certAlias : keyManager.chooseServerAlias(keyType, issuers, socket);
        }

        @Override
        public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
            return certAlias != null ? certAlias : super.chooseEngineServerAlias(keyType, issuers, engine);
        }

        @Override
        public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) {
            return certAlias != null ? certAlias : super.chooseEngineClientAlias(keyTypes, issuers, engine);
        }

        public String[] getClientAliases(String keyType, Principal[] issuers) {
            return keyManager.getClientAliases(keyType, issuers);
        }

        public String[] getServerAliases(String keyType, Principal[] issuers) {
            return keyManager.getServerAliases(keyType, issuers);
        }

        public X509Certificate[] getCertificateChain(String alias) {
            return keyManager.getCertificateChain(alias);
        }

        public PrivateKey getPrivateKey(String alias) {
            return keyManager.getPrivateKey(alias);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy