com.continuent.tungsten.common.security.KeystoreManagerCtrl.java Source code

Java tutorial

Introduction

Here is the source code for com.continuent.tungsten.common.security.KeystoreManagerCtrl.java

Source

/**
 * VMware Continuent Tungsten Replicator
 * Copyright (C) 2015 VMware, Inc. All rights reserved.
 *
 * 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.
 *
 * Initial developer(s): Ludovic Launer
 * Contributor(s): 
 */

package com.continuent.tungsten.common.security;

import java.security.UnrecoverableKeyException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Logger;

import com.continuent.tungsten.common.security.SecurityConf.KEYSTORE_TYPE;

/**
 * Application to manage passwords and users
 * 
 * @author <a href="mailto:ludovic.launer@continuent.com">Ludovic Launer</a>
 * @version 1.0
 */
public class KeystoreManagerCtrl {

    private static Logger logger = Logger.getLogger(KeystoreManagerCtrl.class);

    private static KeystoreManagerCtrl keystoreManager = null;

    private Options helpOptions = new Options();
    private Options options = new Options();

    // --- Options overriding elements in security.properties ---
    private String keystoreLocation = null;
    private String keyAlias = null;
    private KEYSTORE_TYPE keyType = null;

    private String keystorePassword = null;
    private String keyPassword = null;

    // -- Define constants for command line arguments ---
    private static final String HELP = "help";
    private static final String _HELP = "h";
    private static final String _CHECK = "c";
    private static final String CHECK = "check";
    private static final String KEYSTORE_LOCATION = "keystore.location";
    private static final String _KEYSTORE_LOCATION = "ks";
    private static final String KEYSTORE_PASSWORD = "keystore.password";
    private static final String _KEYSTORE_PASSWORD = "ksp";
    private static final String KEY_ALIAS = "key.alias";
    private static final String _KEY_ALIAS = "ka";
    private static final String KEY_TYPE = "key.type";
    private static final String _KEY_TYPE = "kt";
    private static final String KEY_PASSWORD = "key.password";
    private static final String _KEY_PASSWORD = "kp";

    private static Option check;

    // --- Exit codes ---
    public enum EXIT_CODE {
        EXIT_OK(0), EXIT_ERROR(1);

        final int value;

        private EXIT_CODE(int value) {
            this.value = value;
        }
    }

    /**
     * Setup command line options
     */
    @SuppressWarnings("static-access")
    private void setupCommandLine() {
        // --- Options on the command line ---
        Option help = OptionBuilder.withLongOpt(HELP).withDescription("Displays this message").create(_HELP);

        // Mutually excluding options
        OptionGroup optionGroup = new OptionGroup();
        check = OptionBuilder.withLongOpt(CHECK).hasArgs(0).withDescription("Checks key password inside a keystore")
                .create(_CHECK);

        optionGroup.addOption(check);
        optionGroup.setRequired(true); // At least 1 command required

        // --- Options replacing parameters from security.properties ---
        Option keystoreLocation = OptionBuilder.withLongOpt(KEYSTORE_LOCATION).withArgName("filename").hasArg()
                .withDescription("Location of the keystore file").isRequired().create(_KEYSTORE_LOCATION);
        Option keyAlias = OptionBuilder.withLongOpt(KEY_ALIAS).withArgName("key alias").hasArg().isRequired()
                .withDescription("Alias for the key").create(_KEY_ALIAS);
        Option keyType = OptionBuilder.withLongOpt(KEY_TYPE).withArgName("key type").hasArg().isRequired()
                .withDescription(MessageFormat.format("Type of key: {0}", KEYSTORE_TYPE.getListValues("|")))
                .create(_KEY_TYPE);

        Option keystorePassword = OptionBuilder.withLongOpt(KEYSTORE_PASSWORD).withArgName("password").hasArg()
                .withDescription("Password for the keystore file").create(_KEYSTORE_PASSWORD);
        Option keyPassword = OptionBuilder.withLongOpt(KEY_PASSWORD).withArgName("key password").hasArg()
                .withDescription("Password for the key").create(_KEY_PASSWORD);

        // --- Add options to the list ---
        // --- Help
        this.helpOptions.addOption(help);

        // --- Program command line options
        this.options.addOptionGroup(optionGroup);
        this.options.addOption(help);
        this.options.addOption(keystoreLocation);
        this.options.addOption(keyAlias);
        this.options.addOption(keyType);
        this.options.addOption(keystorePassword);
        this.options.addOption(keyPassword);
    }

    /**
     * Creates a new <code>PasswordManager</code> object
     */
    public KeystoreManagerCtrl() {
        this.setupCommandLine();
    }

    /**
     * Password Manager entry point
     * 
     * @param argv
     * @throws Exception
     */
    public static void main(String argv[]) throws Exception {
        keystoreManager = new KeystoreManagerCtrl();

        // --- Options ---
        CommandLine line = null;

        try {
            CommandLineParser parser = new GnuParser();
            // --- Parse the command line arguments ---

            // --- Help
            line = parser.parse(keystoreManager.helpOptions, argv, true);
            if (line.hasOption(_HELP)) {
                DisplayHelpAndExit(EXIT_CODE.EXIT_OK);
            }

            // --- Program command line options
            line = parser.parse(keystoreManager.options, argv);

            // --- Compulsory arguments : Get options ---
            if (line.hasOption(_KEYSTORE_LOCATION))
                keystoreManager.keystoreLocation = line.getOptionValue(_KEYSTORE_LOCATION);

            if (line.hasOption(_KEY_ALIAS))
                keystoreManager.keyAlias = line.getOptionValue(_KEY_ALIAS);

            if (line.hasOption(_KEY_TYPE)) {
                String keyType = line.getOptionValue(_KEY_TYPE);
                // This will throw an exception if the type is not recognised
                KEYSTORE_TYPE certificateTYpe = KEYSTORE_TYPE.fromString(keyType);
                keystoreManager.keyType = certificateTYpe;
            }

            if (line.hasOption(_KEYSTORE_PASSWORD))
                keystoreManager.keystorePassword = line.getOptionValue(_KEYSTORE_PASSWORD);
            if (line.hasOption(_KEY_PASSWORD))
                keystoreManager.keyPassword = line.getOptionValue(_KEY_PASSWORD);

        } catch (ParseException exp) {
            logger.error(exp.getMessage());
            DisplayHelpAndExit(EXIT_CODE.EXIT_ERROR);
        } catch (Exception e) {
            // Workaround for Junit test
            if (e.toString().contains("CheckExitCalled")) {
                throw e;
            } else
            // Normal behaviour
            {
                logger.error(e.getMessage());
                Exit(EXIT_CODE.EXIT_ERROR);
            }

        }

        // --- Perform commands ---

        // ######### Check password ##########
        if (line.hasOption(_CHECK)) {
            try {
                if (keystoreManager.keystorePassword == null || keystoreManager.keyPassword == null) {
                    // --- Get passwords from stdin if not given on command line
                    List<String> listPrompts = Arrays.asList("Keystore password:",
                            MessageFormat.format("Password for key {0}:", keystoreManager.keyAlias));
                    List<String> listUserInput = keystoreManager.getUserInputFromStdin(listPrompts);

                    if (listUserInput.size() == listPrompts.size()) {
                        keystoreManager.keystorePassword = listUserInput.get(0);
                        keystoreManager.keyPassword = listUserInput.get(1);
                    } else {
                        throw new NoSuchElementException();
                    }
                }
                try {
                    // --- Check that all keys in keystore use the keystore
                    // password
                    logger.info(MessageFormat.format("Using keystore:{0}", keystoreManager.keystoreLocation));

                    SecurityHelper.checkKeyStorePasswords(keystoreManager.keystoreLocation, keystoreManager.keyType,
                            keystoreManager.keystorePassword, keystoreManager.keyAlias,
                            keystoreManager.keyPassword);
                    logger.info(MessageFormat.format("OK : Identical password for Keystore and key={0}",
                            keystoreManager.keyAlias));
                } catch (UnrecoverableKeyException uke) {
                    logger.error(MessageFormat.format("At least 1 key has a wrong password.{0}", uke.getMessage()));
                    Exit(EXIT_CODE.EXIT_ERROR);
                } catch (Exception e) {
                    logger.error(MessageFormat.format("{0}", e.getMessage()));
                    Exit(EXIT_CODE.EXIT_ERROR);
                }

            } catch (NoSuchElementException nse) {
                logger.error(nse.getMessage());
                Exit(EXIT_CODE.EXIT_ERROR);
            } catch (Exception e) {
                logger.error(MessageFormat.format("Error while running the program: {0}", e.getMessage()));
                Exit(EXIT_CODE.EXIT_ERROR);
            }
        }

        Exit(EXIT_CODE.EXIT_OK);
    }

    /**
     * Reads user input from stdin
     * 
     * @param listPrompts list of prompts to display, asking for user input
     * @return a list containing user inputs
     */
    private List<String> getUserInputFromStdin(List<String> listPrompts) {
        List<String> listUserInput = new ArrayList<String>();

        Scanner console = new Scanner(System.in);
        Scanner lineTokenizer = null;

        for (String prompt : listPrompts) {
            System.out.println(prompt);

            try {
                lineTokenizer = new Scanner(console.nextLine());
            } catch (NoSuchElementException nse) {
                console.close();
                throw new NoSuchElementException(MessageFormat.format("Missing user input=  {0}", prompt));
            }

            if (lineTokenizer.hasNext()) {
                String userInput = lineTokenizer.next();
                listUserInput.add(userInput);
            }
        }

        console.close();
        return listUserInput;
    }

    /**
     * Display the program help and exits
     */
    private static void DisplayHelpAndExit(EXIT_CODE exitCode) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.setWidth(120);
        formatter.printHelp("tkeystoremanager", keystoreManager.options);
        Exit(exitCode);
    }

    private static void Exit(EXIT_CODE exitCode) {
        System.exit(exitCode.value);
    }

}