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

com.clickhouse.client.config.ClickHouseDefaultSslContextProvider Maven / Gradle / Ivy

There is a newer version: 0.7.1-patch1
Show newest version
package com.clickhouse.client.config;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Optional;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import com.clickhouse.client.ClickHouseConfig;
import com.clickhouse.client.ClickHouseSslContextProvider;
import com.clickhouse.data.ClickHouseUtils;

public class ClickHouseDefaultSslContextProvider implements ClickHouseSslContextProvider {
    static final String PEM_HEADER_PREFIX = "---BEGIN ";
    static final String PEM_HEADER_SUFFIX = " PRIVATE KEY---";
    static final String PEM_FOOTER_PREFIX = "---END ";

    /**
     * An insecure {@link javax.net.ssl.TrustManager}, that don't validate the
     * certificate.
     */
    static class NonValidatingTrustManager implements X509TrustManager {
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }

        @Override
        @SuppressWarnings("squid:S4830")
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
            // ignore
        }

        @Override
        @SuppressWarnings("squid:S4830")
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            // ignore
        }
    }

    static String getAlgorithm(String header, String defaultAlg) {
        int startIndex = header.indexOf(PEM_HEADER_PREFIX);
        int endIndex = startIndex < 0 ? startIndex
                : header.indexOf(PEM_HEADER_SUFFIX, (startIndex += PEM_HEADER_PREFIX.length()));
        return startIndex < endIndex ? header.substring(startIndex, endIndex) : defaultAlg;
    }

    static PrivateKey getPrivateKey(String keyFile)
            throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        String algorithm = (String) ClickHouseDefaults.SSL_KEY_ALGORITHM.getEffectiveDefaultValue();
        StringBuilder builder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(ClickHouseUtils.getFileInputStream(keyFile)))) {
            String line = reader.readLine();
            if (line != null) {
                algorithm = getAlgorithm(line, algorithm);

                while ((line = reader.readLine()) != null) {
                    if (line.indexOf(PEM_FOOTER_PREFIX) >= 0) {
                        break;
                    }

                    builder.append(line);
                }
            }
        }
        byte[] encoded = Base64.getDecoder().decode(builder.toString());
        KeyFactory kf = KeyFactory.getInstance(algorithm);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        return kf.generatePrivate(keySpec);
    }

    protected KeyStore getKeyStore(String cert, String key) throws NoSuchAlgorithmException, InvalidKeySpecException,
            IOException, CertificateException, KeyStoreException {
        final KeyStore ks;
        try {
            ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null, null); // needed to initialize the key store
        } catch (KeyStoreException e) {
            throw new NoSuchAlgorithmException(
                    ClickHouseUtils.format("%s KeyStore not available", KeyStore.getDefaultType()));
        }

        try (InputStream in = ClickHouseUtils.getFileInputStream(cert)) {
            CertificateFactory factory = CertificateFactory
                    .getInstance((String) ClickHouseDefaults.SSL_CERTIFICATE_TYPE.getEffectiveDefaultValue());
            if (key == null || key.isEmpty()) {
                int index = 1;
                for (Certificate c : factory.generateCertificates(in)) {
                    ks.setCertificateEntry("cert" + (index++), c);
                }
            } else {
                Certificate[] certChain = factory.generateCertificates(in).toArray(new Certificate[0]);
                ks.setKeyEntry("key", getPrivateKey(key), null, certChain);
            }
        }
        return ks;
    }

    protected SSLContext getJavaSslContext(ClickHouseConfig config) throws SSLException {
        ClickHouseSslMode sslMode = config.getSslMode();
        String clientCert = config.getSslCert();
        String clientKey = config.getSslKey();
        String sslRootCert = config.getSslRootCert();

        SSLContext ctx;
        try {
            ctx = SSLContext.getInstance((String) ClickHouseDefaults.SSL_PROTOCOL.getEffectiveDefaultValue());
            TrustManager[] tms = null;
            KeyManager[] kms = null;
            SecureRandom sr = null;

            if (sslMode == ClickHouseSslMode.NONE) {
                tms = new TrustManager[] { new NonValidatingTrustManager() };
                kms = new KeyManager[0];
                sr = new SecureRandom();
            } else if (sslMode == ClickHouseSslMode.STRICT) {
                if (clientCert != null && !clientCert.isEmpty()) {
                    KeyManagerFactory factory = KeyManagerFactory
                            .getInstance(KeyManagerFactory.getDefaultAlgorithm());
                    factory.init(getKeyStore(clientCert, clientKey), null);
                    kms = factory.getKeyManagers();
                }

                if (sslRootCert != null && !sslRootCert.isEmpty()) {
                    TrustManagerFactory factory = TrustManagerFactory
                            .getInstance(TrustManagerFactory.getDefaultAlgorithm());
                    factory.init(getKeyStore(sslRootCert, null));
                    tms = factory.getTrustManagers();
                }
                sr = new SecureRandom();
            } else {
                throw new IllegalArgumentException(ClickHouseUtils.format("unspported ssl mode '%s'", sslMode));
            }

            ctx.init(kms, tms, sr);
        } catch (KeyManagementException | InvalidKeySpecException | NoSuchAlgorithmException | KeyStoreException
                | CertificateException | IOException | UnrecoverableKeyException e) {
            throw new SSLException("Failed to get SSL context", e);
        }

        return ctx;
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Optional getSslContext(Class sslContextClass, ClickHouseConfig config)
            throws SSLException {
        return SSLContext.class == sslContextClass ? Optional.of((T) getJavaSslContext(config)) : Optional.empty();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy