org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.nifi.toolkit.tls.standalone;

import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.security.util.KeystoreType;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition;
import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.manager.TlsCertificateAuthorityManager;
import org.apache.nifi.toolkit.tls.manager.TlsClientManager;
import org.apache.nifi.toolkit.tls.manager.writer.NifiPropertiesTlsClientConfigWriter;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.util.io.pem.PemWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;

public class TlsToolkitStandalone {
    public static final String NIFI_KEY = "nifi-key";
    public static final String NIFI_CERT = "nifi-cert";
    public static final String NIFI_PROPERTIES = "nifi.properties";

    private final Logger logger = LoggerFactory.getLogger(TlsToolkitStandalone.class);
    private final OutputStreamFactory outputStreamFactory;

    public TlsToolkitStandalone() {
        this(FileOutputStream::new);
    }

    public TlsToolkitStandalone(OutputStreamFactory outputStreamFactory) {
        this.outputStreamFactory = outputStreamFactory;
    }

    public void createNifiKeystoresAndTrustStores(StandaloneConfig standaloneConfig)
            throws GeneralSecurityException, IOException {
        File baseDir = standaloneConfig.getBaseDir();
        if (!baseDir.exists() && !baseDir.mkdirs()) {
            throw new IOException(baseDir + " doesn't exist and unable to create it.");
        }

        if (!baseDir.isDirectory()) {
            throw new IOException("Expected directory to output to");
        }

        String signingAlgorithm = standaloneConfig.getSigningAlgorithm();
        int days = standaloneConfig.getDays();
        String keyPairAlgorithm = standaloneConfig.getKeyPairAlgorithm();
        int keySize = standaloneConfig.getKeySize();

        File nifiCert = new File(baseDir, NIFI_CERT + ".pem");
        File nifiKey = new File(baseDir, NIFI_KEY + ".key");

        X509Certificate certificate;
        KeyPair caKeyPair;

        if (logger.isInfoEnabled()) {
            logger.info("Running standalone certificate generation with output directory " + baseDir);
        }
        if (nifiCert.exists()) {
            if (!nifiKey.exists()) {
                throw new IOException(nifiCert + " exists already, but " + nifiKey
                        + " does not, we need both certificate and key to continue with an existing CA.");
            }
            try (FileReader pemEncodedCertificate = new FileReader(nifiCert)) {
                certificate = TlsHelper.parseCertificate(pemEncodedCertificate);
            }
            try (FileReader pemEncodedKeyPair = new FileReader(nifiKey)) {
                caKeyPair = TlsHelper.parseKeyPair(pemEncodedKeyPair);
            }

            certificate.verify(caKeyPair.getPublic());
            if (!caKeyPair.getPublic().equals(certificate.getPublicKey())) {
                throw new IOException("Expected " + nifiKey + " to correspond to CA certificate at " + nifiCert);
            }

            if (logger.isInfoEnabled()) {
                logger.info("Using existing CA certificate " + nifiCert + " and key " + nifiKey);
            }
        } else if (nifiKey.exists()) {
            throw new IOException(nifiKey + " exists already, but " + nifiCert
                    + " does not, we need both certificate and key to continue with an existing CA.");
        } else {
            TlsCertificateAuthorityManager tlsCertificateAuthorityManager = new TlsCertificateAuthorityManager(
                    standaloneConfig);
            KeyStore.PrivateKeyEntry privateKeyEntry = tlsCertificateAuthorityManager
                    .getOrGenerateCertificateAuthority();
            certificate = (X509Certificate) privateKeyEntry.getCertificateChain()[0];
            caKeyPair = new KeyPair(certificate.getPublicKey(), privateKeyEntry.getPrivateKey());

            try (PemWriter pemWriter = new PemWriter(
                    new OutputStreamWriter(outputStreamFactory.create(nifiCert)))) {
                pemWriter.writeObject(new JcaMiscPEMGenerator(certificate));
            }

            try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStreamFactory.create(nifiKey)))) {
                pemWriter.writeObject(new JcaMiscPEMGenerator(caKeyPair));
            }

            if (logger.isInfoEnabled()) {
                logger.info("Generated new CA certificate " + nifiCert + " and key " + nifiKey);
            }
        }

        NiFiPropertiesWriterFactory niFiPropertiesWriterFactory = standaloneConfig.getNiFiPropertiesWriterFactory();
        boolean overwrite = standaloneConfig.isOverwrite();

        List<InstanceDefinition> instanceDefinitions = standaloneConfig.getInstanceDefinitions();
        if (instanceDefinitions.isEmpty() && logger.isInfoEnabled()) {
            logger.info("No " + TlsToolkitStandaloneCommandLine.HOSTNAMES_ARG
                    + " specified, not generating any host certificates or configuration.");
        }
        for (InstanceDefinition instanceDefinition : instanceDefinitions) {
            String hostname = instanceDefinition.getHostname();
            File hostDir;
            int hostIdentifierNumber = instanceDefinition.getInstanceIdentifier().getNumber();
            if (hostIdentifierNumber == 1) {
                hostDir = new File(baseDir, hostname);
            } else {
                hostDir = new File(baseDir, hostname + "_" + hostIdentifierNumber);
            }

            TlsClientConfig tlsClientConfig = new TlsClientConfig(standaloneConfig);
            File keystore = new File(hostDir, "keystore." + tlsClientConfig.getKeyStoreType().toLowerCase());
            File truststore = new File(hostDir, "truststore." + tlsClientConfig.getTrustStoreType().toLowerCase());

            if (hostDir.exists()) {
                if (!hostDir.isDirectory()) {
                    throw new IOException(hostDir + " exists but is not a directory.");
                } else if (overwrite) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Overwriting any existing ssl configuration in " + hostDir);
                    }
                    keystore.delete();
                    if (keystore.exists()) {
                        throw new IOException("Keystore " + keystore + " already exists and couldn't be deleted.");
                    }
                    truststore.delete();
                    if (truststore.exists()) {
                        throw new IOException(
                                "Truststore " + truststore + " already exists and couldn't be deleted.");
                    }
                } else {
                    throw new IOException(hostDir + " exists and overwrite is not set.");
                }
            } else if (!hostDir.mkdirs()) {
                throw new IOException("Unable to make directory: " + hostDir.getAbsolutePath());
            } else if (logger.isInfoEnabled()) {
                logger.info("Writing new ssl configuration to " + hostDir);
            }

            tlsClientConfig.setKeyStore(keystore.getAbsolutePath());
            tlsClientConfig.setKeyStorePassword(instanceDefinition.getKeyStorePassword());
            tlsClientConfig.setKeyPassword(instanceDefinition.getKeyPassword());
            tlsClientConfig.setTrustStore(truststore.getAbsolutePath());
            tlsClientConfig.setTrustStorePassword(instanceDefinition.getTrustStorePassword());
            TlsClientManager tlsClientManager = new TlsClientManager(tlsClientConfig);
            KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
            Extensions sanDnsExtensions = StringUtils.isBlank(tlsClientConfig.getDomainAlternativeNames()) ? null
                    : TlsHelper.createDomainAlternativeNamesExtensions(tlsClientConfig.getDomainAlternativeNames());
            tlsClientManager.addPrivateKeyToKeyStore(keyPair, NIFI_KEY,
                    CertificateUtils.generateIssuedCertificate(tlsClientConfig.calcDefaultDn(hostname),
                            keyPair.getPublic(), sanDnsExtensions, certificate, caKeyPair, signingAlgorithm, days),
                    certificate);
            tlsClientManager.setCertificateEntry(NIFI_CERT, certificate);
            tlsClientManager.addClientConfigurationWriter(
                    new NifiPropertiesTlsClientConfigWriter(niFiPropertiesWriterFactory,
                            new File(hostDir, "nifi.properties"), hostname, instanceDefinition.getNumber()));
            tlsClientManager.write(outputStreamFactory);
            if (logger.isInfoEnabled()) {
                logger.info("Successfully generated TLS configuration for " + hostname + " " + hostIdentifierNumber
                        + " in " + hostDir);
            }
        }

        List<String> clientDns = standaloneConfig.getClientDns();
        if (standaloneConfig.getClientDns().isEmpty() && logger.isInfoEnabled()) {
            logger.info("No " + TlsToolkitStandaloneCommandLine.CLIENT_CERT_DN_ARG
                    + " specified, not generating any client certificates.");
        }

        List<String> clientPasswords = standaloneConfig.getClientPasswords();
        for (int i = 0; i < clientDns.size(); i++) {
            String reorderedDn = CertificateUtils.reorderDn(clientDns.get(i));
            String clientDnFile = getClientDnFile(reorderedDn);
            File clientCertFile = new File(baseDir, clientDnFile + ".p12");

            if (clientCertFile.exists()) {
                if (overwrite) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Overwriting existing client cert " + clientCertFile);
                    }
                } else {
                    throw new IOException(clientCertFile + " exists and overwrite is not set.");
                }
            } else if (logger.isInfoEnabled()) {
                logger.info("Generating new client certificate " + clientCertFile);
            }
            KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
            X509Certificate clientCert = CertificateUtils.generateIssuedCertificate(reorderedDn,
                    keyPair.getPublic(), null, certificate, caKeyPair, signingAlgorithm, days);
            KeyStore keyStore = KeyStoreUtils.getKeyStore(KeystoreType.PKCS12.toString());
            keyStore.load(null, null);
            keyStore.setKeyEntry(NIFI_KEY, keyPair.getPrivate(), null,
                    new Certificate[] { clientCert, certificate });
            String password = TlsHelper.writeKeyStore(keyStore, outputStreamFactory, clientCertFile,
                    clientPasswords.get(i), standaloneConfig.isClientPasswordsGenerated());

            try (FileWriter fileWriter = new FileWriter(new File(baseDir, clientDnFile + ".password"))) {
                fileWriter.write(password);
            }

            if (logger.isInfoEnabled()) {
                logger.info("Successfully generated client certificate " + clientCertFile);
            }
        }

        if (logger.isInfoEnabled()) {
            logger.info("tls-toolkit standalone completed successfully");
        }
    }

    protected static String getClientDnFile(String clientDn) {
        return clientDn.replace(',', '_').replace(' ', '_');
    }
}