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

com.github.netty.protocol.mqtt.MqttSslContextCreator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2012-2018 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * The Apache License v2.0 is available at
 * http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package com.github.netty.protocol.mqtt;

import com.github.netty.core.util.LoggerFactoryX;
import com.github.netty.core.util.LoggerX;
import com.github.netty.protocol.mqtt.config.BrokerConstants;
import com.github.netty.protocol.mqtt.config.IConfig;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.net.URL;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Objects;

/**
 * SSL certificate loader used to open SSL connections (websocket and MQTT-S).
 */
public class MqttSslContextCreator {

    private final IConfig props;
    private LoggerX logger = LoggerFactoryX.getLogger(getClass());

    public MqttSslContextCreator(IConfig props) {
        this.props = Objects.requireNonNull(props);
    }

    /**
     * The OpenSSL provider does not support the {@link KeyManagerFactory}, so we have to lookup the integration
     * certificate and key in order to provide it to OpenSSL.
     * 

* TODO: SNI is currently not supported, we use only the first found private key. */ private static SslContextBuilder builderWithOpenSSLProvider(KeyStore ks, String keyPassword) throws GeneralSecurityException { for (String alias : Collections.list(ks.aliases())) { if (ks.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { PrivateKey key = (PrivateKey) ks.getKey(alias, keyPassword.toCharArray()); Certificate[] chain = ks.getCertificateChain(alias); X509Certificate[] certChain = new X509Certificate[chain.length]; System.arraycopy(chain, 0, certChain, 0, chain.length); return SslContextBuilder.forServer(key, certChain); } } throw new KeyManagementException("the SSL key-store does not contain a private key"); } public SslContext initSSLContext() { logger.debug("Checking SSL configuration properties..."); final String keyPassword = props.getProperty(BrokerConstants.KEY_MANAGER_PASSWORD_PROPERTY_NAME); if (keyPassword == null || keyPassword.isEmpty()) { logger.warn("The key manager password is null or empty. The SSL context won't be initialized."); return null; } try { SslProvider sslProvider = getSSLProvider(); KeyStore ks = loadKeyStore(); SslContextBuilder contextBuilder; switch (sslProvider) { case JDK: contextBuilder = builderWithJdkProvider(ks, keyPassword); break; case OPENSSL: case OPENSSL_REFCNT: contextBuilder = builderWithOpenSSLProvider(ks, keyPassword); break; default: logger.error("unsupported SSL provider " + sslProvider); return null; } // if client authentification is enabled a trustmanager needs to be added to the ServerContext String sNeedsClientAuth = props.getProperty(BrokerConstants.NEED_CLIENT_AUTH, "false"); if (Boolean.valueOf(sNeedsClientAuth)) { addClientAuthentication(ks, contextBuilder); } contextBuilder.sslProvider(sslProvider); SslContext sslContext = contextBuilder.build(); logger.debug("The SSL context has been initialized successfully."); return sslContext; } catch (GeneralSecurityException | IOException ex) { logger.error("Unable to initialize SSL context.", ex); return null; } } private KeyStore loadKeyStore() throws IOException, GeneralSecurityException { final String jksPath = props.getProperty(BrokerConstants.JKS_PATH_PROPERTY_NAME); logger.debug("Initializing SSL context. KeystorePath = {}.", jksPath); if (jksPath == null || jksPath.isEmpty()) { logger.warn("The keystore path is null or empty. The SSL context won't be initialized."); return null; } final String keyStorePassword = props.getProperty(BrokerConstants.KEY_STORE_PASSWORD_PROPERTY_NAME); if (keyStorePassword == null || keyStorePassword.isEmpty()) { logger.warn("The keystore password is null or empty. The SSL context won't be initialized."); return null; } String ksType = props.getProperty(BrokerConstants.KEY_STORE_TYPE, "jks"); final KeyStore keyStore = KeyStore.getInstance(ksType); logger.debug("Loading keystore. KeystorePath = {}.", jksPath); try (InputStream jksInputStream = jksDatastore(jksPath)) { keyStore.load(jksInputStream, keyStorePassword.toCharArray()); } return keyStore; } private SslContextBuilder builderWithJdkProvider(KeyStore ks, String keyPassword) throws GeneralSecurityException { logger.debug("Initializing key manager..."); final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, keyPassword.toCharArray()); logger.debug("Initializing SSL context..."); return SslContextBuilder.forServer(kmf); } private void addClientAuthentication(KeyStore ks, SslContextBuilder contextBuilder) throws NoSuchAlgorithmException, KeyStoreException { logger.warn("Client authentication is enabled. The keystore will be used as a truststore."); // use keystore as truststore, as integration needs to trust certificates signed by the integration certificates TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); contextBuilder.clientAuth(ClientAuth.REQUIRE); contextBuilder.trustManager(tmf); } private SslProvider getSSLProvider() { String providerName = props.getProperty(BrokerConstants.SSL_PROVIDER, SslProvider.JDK.name()); try { return SslProvider.valueOf(providerName); } catch (IllegalArgumentException e) { logger.warn("unknown SSL Provider {}, falling back on JDK provider", providerName); return SslProvider.JDK; } } private InputStream jksDatastore(String jksPath) throws FileNotFoundException { URL jksUrl = getClass().getClassLoader().getResource(jksPath); if (jksUrl != null) { logger.debug("Starting with jks at {}, jks normal {}", jksUrl.toExternalForm(), jksUrl); return getClass().getClassLoader().getResourceAsStream(jksPath); } logger.warn("No keystore has been found in the bundled resources. Scanning filesystem..."); File jksFile = new File(jksPath); if (jksFile.exists()) { logger.debug("Loading external keystore. Url = {}.", jksFile.getAbsolutePath()); return new FileInputStream(jksFile); } throw new FileNotFoundException("The keystore file does not exist. Url = " + jksFile.getAbsolutePath()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy