cloudeventbus.cli.Certs.java Source code

Java tutorial

Introduction

Here is the source code for cloudeventbus.cli.Certs.java

Source

/*
 *   Copyright (c) 2013 Mike Heath.  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.
 *
 */
package cloudeventbus.cli;

import cloudeventbus.Subject;
import cloudeventbus.pki.Certificate;
import cloudeventbus.pki.CertificateChain;
import cloudeventbus.pki.CertificateStoreLoader;
import cloudeventbus.pki.CertificateUtils;
import cloudeventbus.pki.TrustStore;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import org.apache.commons.codec.binary.Base64InputStream;

import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author Mike Heath <elcapo@gmail.com>
 */
// TODO Write and publish docs
public class Certs {

    private static final String CHAIN_CERTIFICATE = "chain-certificate";
    private static final String CREATE_AUTHORITY = "create-authority";
    private static final String CREATE_CLIENT = "create-client";
    private static final String CREATE_SERVER = "create-server";
    private static final String IMPORT_CERTIFICATES = "import-certificates";
    private static final String LIST_AUTHORITIES = "list-authorities";
    private static final String SHOW_CERTIFICATE = "show-certificate";
    private static final String VALIDATE_CERTIFICATE = "validate-certificate";
    private static final String REMOVE_AUTHORITY = "remove-authority";

    public static void main(String[] args) throws Exception {
        final JCommander commander = new JCommander();

        final DefaultOptions options = new DefaultOptions();
        final CreateAuthorityCommand createAuthorityCommand = new CreateAuthorityCommand();
        final CreateClientCommand createClientCommand = new CreateClientCommand();
        final CreateServerCommand createServerCommand = new CreateServerCommand();
        final ChainCertificateCommand chainCertificateCommand = new ChainCertificateCommand();
        final ShowCertificateCommand showCertificateCommand = new ShowCertificateCommand();
        final ImportCertificatesCommand importCertificatesCommand = new ImportCertificatesCommand();
        final RemoveAuthorityCommand removeAuthorityCommand = new RemoveAuthorityCommand();
        final ValidateCommand validateCommand = new ValidateCommand();
        commander.addObject(options);
        commander.addCommand(createAuthorityCommand);
        commander.addCommand(createClientCommand);
        commander.addCommand(createServerCommand);
        commander.addCommand(chainCertificateCommand);
        commander.addCommand(new ListAuthorities());
        commander.addCommand(showCertificateCommand);
        commander.addCommand(importCertificatesCommand);
        commander.addCommand(removeAuthorityCommand);
        commander.addCommand(validateCommand);

        commander.setProgramName("eventbus-certs");

        try {
            commander.parse(args);

            final String command = commander.getParsedCommand();
            if (command == null) {
                commander.usage();
            } else {
                final TrustStore trustStore = CertificateUtils.loadTrustStore(options.trustStore);
                switch (command) {
                case CREATE_AUTHORITY: {
                    final KeyPair keyPair = CertificateUtils.generateKeyPair();
                    CertificateUtils.savePrivateKey(keyPair.getPrivate(), createAuthorityCommand.privateKey);
                    final Certificate certificate = CertificateUtils.generateSelfSignedCertificate(keyPair,
                            getExpirationDate(createAuthorityCommand.expirationDate),
                            Subject.list(createAuthorityCommand.subscribePermissions),
                            Subject.list(createAuthorityCommand.publishPermissions),
                            createAuthorityCommand.comment);
                    trustStore.add(certificate);

                    CertificateUtils.saveCertificates(options.trustStore, trustStore);
                    System.out.println("Created authority certificate.");
                    break;
                }
                case LIST_AUTHORITIES: {
                    displayCertificates(trustStore);
                    break;
                }
                case CREATE_CLIENT:
                    createCertificate(trustStore, Certificate.Type.CLIENT, createClientCommand);
                    break;
                case CREATE_SERVER:
                    createCertificate(trustStore, Certificate.Type.SERVER, createServerCommand);
                    break;
                case SHOW_CERTIFICATE: {
                    final CertificateChain certificates = CertificateUtils
                            .loadCertificateChain(showCertificateCommand.certificate);
                    displayCertificates(certificates);
                    break;
                }
                case CHAIN_CERTIFICATE:
                    chainCertificate(chainCertificateCommand);
                    break;
                case VALIDATE_CERTIFICATE: {
                    final CertificateChain certificates = CertificateUtils
                            .loadCertificateChain(validateCommand.certificate);
                    trustStore.validateCertificateChain(certificates);
                    System.out.println(validateCommand.certificate + " is valid.");
                    break;
                }
                case IMPORT_CERTIFICATES: {
                    final Path path = Paths.get(importCertificatesCommand.certificate);
                    try (final InputStream fileIn = Files.newInputStream(path);
                            final InputStream in = new Base64InputStream(fileIn)) {
                        final Collection<Certificate> certificates = new ArrayList<>();
                        CertificateStoreLoader.load(in, certificates);
                        for (Certificate certificate : certificates) {
                            trustStore.add(certificate);
                        }
                        CertificateUtils.saveCertificates(options.trustStore, trustStore);
                    }
                    break;
                }
                case REMOVE_AUTHORITY:
                    if (trustStore.remove(removeAuthorityCommand.serialNumber)) {
                        System.err.println("Removed certificate from trust store.");
                        CertificateUtils.saveCertificates(options.trustStore, trustStore);
                    } else {
                        System.err.println("Certificate with serial number " + removeAuthorityCommand.serialNumber
                                + " not found.");
                    }
                }
            }
        } catch (ParameterException e) {
            System.err.println(e.getMessage());
            commander.usage(commander.getParsedCommand());
            System.exit(1);
        }
    }

    private static void displayCertificates(Iterable<Certificate> certificates) {
        System.out.println("=============================================================");
        for (Certificate certificate : certificates) {
            System.out.println("Serial number:         " + certificate.getSerialNumber());
            System.out.println("Issuer:                " + certificate.getIssuer());
            System.out.println("Type:                  " + certificate.getType());
            final String expirationString;
            if (certificate.getExpirationDate() == -1) {
                expirationString = "Never";
            } else if (certificate.getExpirationDate() < System.currentTimeMillis()) {
                expirationString = "Expired";
            } else {
                final long seconds = (certificate.getExpirationDate() - System.currentTimeMillis()) / 1000;
                expirationString = "Expires in " + formatTime(seconds);
            }
            System.out.println("Expiration:            " + expirationString);
            System.out.println("Publish permissions:   " + certificate.getPublishPermissions());
            System.out.println("Subscribe permissions: " + certificate.getSubscribePermissions());
            System.out.println("Comment: " + certificate.getComment());
            System.out.println("=============================================================");
        }
    }

    private static String formatTime(long seconds) {
        final long days = seconds / TimeUnit.DAYS.toSeconds(1);
        seconds -= TimeUnit.DAYS.toSeconds(days);
        final long hours = seconds / TimeUnit.HOURS.toSeconds(1);
        seconds -= TimeUnit.HOURS.toSeconds(hours);
        final long minutes = seconds / TimeUnit.MINUTES.toSeconds(1);
        seconds -= TimeUnit.MINUTES.toSeconds(minutes);
        return String.format("%dd:%dh:%dm:%ds", days, hours, minutes, seconds);
    }

    private static void createCertificate(TrustStore trustStore, Certificate.Type type,
            AbstractCreateClientServerCommand createCommand) throws Exception {
        final Certificate issuerCertificate = trustStore.get(createCommand.issuer);
        if (issuerCertificate == null) {
            throw new IllegalArgumentException(
                    "No certificate found in trust store with serial number " + createCommand.issuer);
        }
        final PrivateKey issuerPrivateKey = CertificateUtils.loadPrivateKey(createCommand.issuerPrivateKey);
        final KeyPair keyPair = CertificateUtils.generateKeyPair();
        CertificateUtils.savePrivateKey(keyPair.getPrivate(), createCommand.privateKey);
        final Certificate certificate = CertificateUtils.generateSignedCertificate(issuerCertificate,
                issuerPrivateKey, keyPair.getPublic(), type, getExpirationDate(createCommand.expirationDate),
                Subject.list(createCommand.subscribePermissions), Subject.list(createCommand.publishPermissions),
                createCommand.comment);
        final CertificateChain chain = new CertificateChain(certificate);
        CertificateUtils.saveCertificates(createCommand.certificate, chain);
    }

    private static void chainCertificate(ChainCertificateCommand command) throws Exception {
        final CertificateChain certificates = CertificateUtils.loadCertificateChain(command.existingCertificate);
        final PrivateKey privateKey = CertificateUtils.loadPrivateKey(command.existingPrivateKey);

        final KeyPair keyPair = CertificateUtils.generateKeyPair();
        final Certificate certificate = CertificateUtils.generateSignedCertificate(certificates.getLast(),
                privateKey, keyPair.getPublic(), certificates.getLast().getType(),
                getExpirationDate(command.expirationDate), Subject.list(command.subscribePermissions),
                Subject.list(command.publishPermissions), command.comment);
        certificates.add(certificate);
        CertificateUtils.saveCertificates(command.certificate, certificates);
        CertificateUtils.savePrivateKey(keyPair.getPrivate(), command.privateKey);
    }

    private static long getExpirationDate(long expirationDate) {
        return expirationDate == -1 ? -1 : System.currentTimeMillis() + expirationDate * 1000;
    }

    private static class AbstractCreateCommand {
        @Parameter(names = "-privateKey", description = "The file to store the private key for the certificate being created.", required = true)
        String privateKey;

        @Parameter(names = "-expirationDate", description = "The time, in seconds, until the certifcate expires. Use -1 for no expiration.")
        long expirationDate = -1;

        @Parameter(names = "-comment", description = "A comment to associate with the certificate.")
        String comment;

        @Parameter(names = "-publishPermission", description = "Specifies a subject on which the certificate is authorized to publish.")
        List<String> publishPermissions = Arrays.asList("*");

        @Parameter(names = "-subscribePermission", description = "Specifies a subject on which the certificate is authorized to subscribe.")
        List<String> subscribePermissions = Arrays.asList("*");
    }

    @Parameters(commandNames = CREATE_AUTHORITY, commandDescription = "Creates an authority certificate and adds it to the trust store")
    private static class CreateAuthorityCommand extends AbstractCreateCommand {

    }

    private static abstract class AbstractCreateClientServerCommand extends AbstractCreateCommand {

        @Parameter(names = "-issuer", description = "The id of the issuer certificates in the trust store", required = true)
        long issuer;

        @Parameter(names = "-issuerPrivateKey", description = "The file storing the issuer certificate's private key.", required = true)
        String issuerPrivateKey;

        @Parameter(names = "-certificate", description = "The file in which to store the certificate", required = true)
        String certificate;
    }

    @Parameters(commandNames = CREATE_CLIENT, commandDescription = "Creates a client certificate")
    private static class CreateClientCommand extends AbstractCreateClientServerCommand {
    }

    @Parameters(commandNames = CREATE_SERVER, commandDescription = "Creates a server certificate")
    private static class CreateServerCommand extends AbstractCreateClientServerCommand {
    }

    @Parameters(commandNames = CHAIN_CERTIFICATE, commandDescription = "Chains a new certificate to an existing certificate or certificate chain")
    private static class ChainCertificateCommand extends AbstractCreateCommand {

        @Parameter(names = "-existingPrivateKey", description = "The private key of the certificate being chained or the private key of the last certificate in the chain", required = true)
        String existingPrivateKey;

        @Parameter(names = "-existingCertificate", description = "The file name of the existing certificate or certificate chain", required = true)
        String existingCertificate;

        @Parameter(names = "-certificate", description = "The file in which to store the new certificate chain", required = true)
        String certificate;
    }

    @Parameters(commandNames = LIST_AUTHORITIES, commandDescription = "List the certificates in the trust store.")
    private static class ListAuthorities {
    }

    @Parameters(commandNames = SHOW_CERTIFICATE, commandDescription = "Displays the contents of a certificate.")
    private static class ShowCertificateCommand {
        @Parameter(names = "-certificate", description = "The certificate or certificate chain to display", required = true)
        String certificate;
    }

    @Parameters(commandNames = IMPORT_CERTIFICATES, commandDescription = "Imports certificates into the trust store from another trust store or certificate.")
    private static class ImportCertificatesCommand {
        @Parameter(names = "-certificate", description = "The trust store, certificate, certificate chain to import", required = true)
        String certificate;
    }

    @Parameters(commandNames = VALIDATE_CERTIFICATE, commandDescription = "Validates a certificate or certificate chain against the trust store")
    private static class ValidateCommand {
        @Parameter(names = "-certificate", description = "The certificate or certificate chain to validate", required = true)
        String certificate;
    }

    @Parameters(commandNames = REMOVE_AUTHORITY, commandDescription = "Removes an authority certificate from the trust store")
    private static class RemoveAuthorityCommand {
        @Parameter(names = "-serialNumber", description = "The serial number of the certificate to be removed", required = true)
        Long serialNumber;
    }

}