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

org.eclipse.ditto.connectivity.service.messaging.internal.ssl.TrustManagerFactoryFactory Maven / Gradle / Ivy

/*
 * Copyright (c) 2019 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.ditto.connectivity.service.messaging.internal.ssl;

import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertPathBuilder;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.X509CertSelector;
import java.util.Collection;

import javax.annotation.Nullable;
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.TrustManagerFactory;

import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.json.JsonPointer;

import io.netty.handler.ssl.util.InsecureTrustManagerFactory;

/**
 * Creates {@link javax.net.ssl.TrustManagerFactory}s from different sources.
 */
public final class TrustManagerFactoryFactory {

    private static final String PKIX = "PKIX";
    private static final KeyStore DEFAULT_CA_KEYSTORE = loadDefaultCAKeystore();
    private static final String CERTIFICATE_LABEL = "CERTIFICATE";

    private static final CertificateFactory X509_CERTIFICATE_FACTORY;
    private final KeyStoreFactory keyStoreFactory;
    private final ExceptionMapper exceptionMapper;

    static {
        try {
            X509_CERTIFICATE_FACTORY = CertificateFactory.getInstance("X.509");
        } catch (final CertificateException e) {
            throw new Error("FATAL: failed to load X.509 certificate factory", e);
        }
    }

    TrustManagerFactoryFactory(final ExceptionMapper exceptionMapper) {
        this.exceptionMapper = checkNotNull(exceptionMapper, "exceptionMapper");
        keyStoreFactory = new KeyStoreFactory(exceptionMapper);
    }

    public static TrustManagerFactoryFactory getInstance(final DittoHeaders dittoHeaders) {
        return new TrustManagerFactoryFactory(ExceptionMapper.forTrustedCertificates(dittoHeaders));
    }

    public static TrustManagerFactoryFactory getInstance(final ExceptionMapper exceptionMapper) {
        return new TrustManagerFactoryFactory(exceptionMapper);
    }

    public TrustManagerFactory newTrustManagerFactory(@Nullable final String trustedCertificates,
            final boolean checkRevocation) {
        return handleExceptions(() -> createTrustManagerFactory(trustedCertificates, checkRevocation));
    }

    public TrustManagerFactory newTrustManagerFactory(final Connection connection, final boolean checkRevocation) {
        final String trustedCertificates = connection.getTrustedCertificates().orElse(null);
        final TrustManagerFactory factory = newTrustManagerFactory(trustedCertificates, checkRevocation);
        if (connection.isValidateCertificates()) {
            return factory;
        } else {
            return AcceptAnyTrustManager.factory(factory);
        }
    }

    public TrustManagerFactory newInsecureTrustManagerFactory() {
        return InsecureTrustManagerFactory.INSTANCE;
    }

    private TrustManagerFactory createTrustManagerFactory(
            @Nullable final String trustedCertificates,
            final boolean checkForRevocation)
            throws NoSuchAlgorithmException, CertificateException, KeyStoreException,
            InvalidAlgorithmParameterException {
        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PKIX);
        if (trustedCertificates != null) {
            final KeyStore keystore = keyStoreFactory.newKeystore();
            final Collection caCerts;
            final byte[] caCertsPem = trustedCertificates.getBytes(StandardCharsets.US_ASCII);
            caCerts = X509_CERTIFICATE_FACTORY.generateCertificates(new ByteArrayInputStream(caCertsPem));
            long cnt = 0;
            for (final Certificate caCert : caCerts) {
                keystore.setCertificateEntry("ca-" + cnt++, caCert);
            }
            trustManagerFactory.init(keystore);
        } else {
            // standard CAs; add revocation check
            final PKIXBuilderParameters parameters =
                    new PKIXBuilderParameters(DEFAULT_CA_KEYSTORE, new X509CertSelector());
            if (checkForRevocation) {
                parameters.addCertPathChecker(
                        (PKIXCertPathChecker) CertPathBuilder.getInstance(PKIX).getRevocationChecker());
            } else {
                parameters.addCertPathChecker(NoRevocationChecker.getInstance());
            }
            trustManagerFactory.init(new CertPathTrustManagerParameters(parameters));
        }
        return trustManagerFactory;
    }

    private static KeyStore loadDefaultCAKeystore() {
        try {
            final String javaHome = System.getProperty("java.home");
            final String cacerts = javaHome + "/lib/security/cacerts";
            final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
            try (final FileInputStream cacertsStream = new FileInputStream(cacerts)) {
                keystore.load(cacertsStream, "changeit".toCharArray());
            }
            return keystore;
        } catch (final KeyStoreException e) {
            throw new Error("FATAL: Cannot create default CA keystore");
        } catch (final IOException e) {
            throw new Error("FATAL: Cannot read default CA keystore");
        } catch (final NoSuchAlgorithmException | CertificateException e) {
            throw new Error("FATAL: Cannot load default CA keystore");
        }
    }

    /**
     * Handles common ssl exceptions and maps them to Ditto exceptions.
     *
     * @param supplier the supplier that may throw an exception
     * @param  the result type
     * @return the result if no exception occurred
     */
    private  T handleExceptions(final ThrowingSupplier supplier) {
        try {
            return supplier.get();
        } catch (final CertificateException e) {
            final JsonPointer errorLocation = Connection.JsonFields.TRUSTED_CERTIFICATES.getPointer();
            throw exceptionMapper.badFormat(errorLocation, CERTIFICATE_LABEL, "DER")
                    .cause(e)
                    .build();
        } catch (final KeyStoreException e) {
            throw exceptionMapper.fatalError("Engine failed to configure trusted CA certificates")
                    .cause(e)
                    .build();
        } catch (final NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
            throw exceptionMapper.fatalError("Failed to start TLS engine")
                    .cause(e)
                    .build();
        }
    }

    @FunctionalInterface
    public interface ThrowingSupplier {

        /**
         * @return the result.
         */
        T get() throws CertificateException, KeyStoreException, NoSuchAlgorithmException,
                InvalidAlgorithmParameterException;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy