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

edu.vt.middleware.crypt.KeyStoreCli Maven / Gradle / Ivy

There is a newer version: 2.2
Show newest version
/*
  $Id$

  Copyright (C) 2007-2011 Virginia Tech.
  All rights reserved.

  SEE LICENSE FOR MORE INFORMATION

  Author:  Middleware Services
  Email:   [email protected]
  Version: $Revision$
  Updated: $Date$
*/
package edu.vt.middleware.crypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import edu.vt.middleware.crypt.digest.MD5;
import edu.vt.middleware.crypt.digest.SHA1;
import edu.vt.middleware.crypt.util.CryptReader;
import edu.vt.middleware.crypt.util.CryptWriter;
import edu.vt.middleware.crypt.util.HexConverter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;

/**
 * Command line interface for keystore operations.
 *
 * @author  Middleware Services
 * @version  $Revision: 84 $
 */
public class KeyStoreCli extends AbstractCli
{

  /** Default key algorithm name. */
  public static final String DEFAULT_KEY_ALGORITHM = "RSA";

  /** Option for listing keystore contents. */
  protected static final String OPT_LIST = "list";

  /** Option for importing keypair into keystore. */
  protected static final String OPT_IMPORT = "import";

  /** Option for exporting keypair from keystore. */
  protected static final String OPT_EXPORT = "export";

  /** Option for specifying keystore file. */
  protected static final String OPT_STORE = "keystore";

  /** Option for specifying keystore type. */
  protected static final String OPT_TYPE = "storetype";

  /** Option for specifying keystore password. */
  protected static final String OPT_PASS = "storepass";

  /** Option for specifying alias for keystore entry to import/export. */
  protected static final String OPT_ALIAS = "alias";

  /** Option for specifying certificate file to import/export. */
  protected static final String OPT_CERT = "cert";

  /** Option for specifying key file to import/export. */
  protected static final String OPT_KEY = "key";

  /** Option for specifying key cipher algorithm. */
  protected static final String OPT_KEYALG = "keyalg";

  /** Name of operation provided by this class. */
  private static final String COMMAND_NAME = "keystore";

  /** For calculating MD5 cert fingerprints. */
  private final MD5 md5 = new MD5();

  /** For calculating SHA-1 cert fingerprints. */
  private final SHA1 sha1 = new SHA1();

  /** For pretty-printing cert signature fingerprint in HEX. */
  private final HexConverter hexConv = new HexConverter(true);


  /**
   * CLI entry point method.
   *
   * @param  args  Command line arguments.
   */
  public static void main(final String[] args)
  {
    new KeyStoreCli().performAction(args);
  }


  /** {@inheritDoc} */
  protected void initOptions()
  {
    super.initOptions();

    final Option keystore = new Option(OPT_STORE, true, "keystore file");
    keystore.setArgName("filepath");
    keystore.setOptionalArg(false);

    final Option pass = new Option(OPT_PASS, true, "keystore password");
    pass.setArgName("password");
    pass.setOptionalArg(false);

    final Option type = new Option(
      OPT_TYPE,
      true,
      "keystore type, e.g. BKS (default), JKS");
    type.setArgName("name");
    type.setOptionalArg(false);

    final Option alias = new Option(
      OPT_ALIAS,
      true,
      "alias assigned to imported item or alias of item to export");
    alias.setArgName("name");
    alias.setOptionalArg(false);

    final Option cert = new Option(
      OPT_CERT,
      true,
      "X.509 certificate file; " +
      "encoding determined by file extension (der|pem)");
    cert.setArgName("filepath");
    cert.setOptionalArg(false);

    final Option key = new Option(
      OPT_KEY,
      true,
      "DER-encoded PKCS#8 or PEM-encoded SSLeay private key; " +
      "encoding determined by file extension (der|pem)");
    key.setArgName("filepath");
    key.setOptionalArg(false);

    final Option keyalg = new Option(
      OPT_KEYALG,
      true,
      "private key algorithm name; assumes RSA if not specified");
    keyalg.setArgName("algorithm");
    keyalg.setOptionalArg(false);

    options.addOption(keystore);
    options.addOption(pass);
    options.addOption(type);
    options.addOption(alias);
    options.addOption(cert);
    options.addOption(key);
    options.addOption(keyalg);
    options.addOption(new Option(OPT_LIST, "list keystore contents"));
    options.addOption(new Option(OPT_IMPORT, "import cert or cert/key pair"));
    options.addOption(new Option(OPT_EXPORT, "export cert or cert/key pair"));
  }


  /** {@inheritDoc} */
  protected void dispatch(final CommandLine line)
    throws Exception
  {
    if (line.hasOption(OPT_LIST)) {
      list(line);
    } else if (line.hasOption(OPT_IMPORT)) {
      doImport(line);
    } else if (line.hasOption(OPT_EXPORT)) {
      doExport(line);
    } else {
      printHelp();
    }
  }


  /**
   * Lists keystore contents on STDOUT. Output is similar to keytool -list -v.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On errors.
   */
  protected void list(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final KeyStore store = readKeyStore(line);
    final Enumeration aliases = store.aliases();
    System.out.println("");
    while (aliases.hasMoreElements()) {
      final String alias = aliases.nextElement();
      System.out.println("Alias name: " + alias);
      System.out.println("Creation date: " + store.getCreationDate(alias));
      if (store.isKeyEntry(alias)) {
        System.out.println("Entry type: keyEntry");

        final Certificate[] chain = store.getCertificateChain(alias);
        System.out.println("Certificate chain length: " + chain.length);
        for (int i = 0; i < chain.length; i++) {
          System.out.println("===== Certificate [" + i + "] =====");
          printCertificate(chain[i]);
        }
      } else {
        System.out.println("Entry type: trustedCertEntry");
        System.out.println("Certificate details:");
        printCertificate(store.getCertificate(alias));
      }
      System.out.println("");
      System.out.println("");
    }
  }


  /**
   * Imports a certificate or key pair into the keystore.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On errors.
   */
  protected void doImport(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final KeyStore store = readKeyStore(line);
    final String alias = line.getOptionValue(OPT_ALIAS);
    final File certFile = new File(line.getOptionValue(OPT_CERT));
    if (line.hasOption(OPT_KEY)) {
      final File keyFile = new File(line.getOptionValue(OPT_KEY));
      final char[] passChars = line.getOptionValue(OPT_PASS).toCharArray();
      final PrivateKey key = CryptReader.readPrivateKey(keyFile);
      final Certificate[] chain = CryptReader.readCertificateChain(certFile);
      System.err.println(
        "Read certificate chain of length " + chain.length + ":");
      for (int i = 0; i < chain.length; i++) {
        System.out.println("===== Certificate [" + i + "] =====");
        printCertificate(chain[i]);
      }
      store.setKeyEntry(alias, key, passChars, chain);
      System.err.println("Imported key entry " + alias);
    } else {
      final Certificate cert = CryptReader.readCertificate(certFile);
      System.err.println("Read certificate:");
      printCertificate(cert);
      store.setCertificateEntry(alias, cert);
      System.err.println("Imported trusted cert entry " + alias);
    }

    final OutputStream os = new FileOutputStream(
      new File(line.getOptionValue(OPT_STORE)));
    try {
      store.store(os, line.getOptionValue(OPT_PASS).toCharArray());
    } finally {
      os.close();
    }
  }


  /**
   * Exports a certificate or key pair from the keystore.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On errors.
   */
  protected void doExport(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final KeyStore store = readKeyStore(line);
    final String alias = line.getOptionValue(OPT_ALIAS);
    boolean wroteData = false;
    if (line.hasOption(OPT_CERT)) {
      final File certFile = new File(line.getOptionValue(OPT_CERT));
      final Certificate[] certs = store.getCertificateChain(alias);
      if (certs != null) {
        if (certFile.getName().endsWith(PEM_SUFFIX)) {
          CryptWriter.writePemCertificates(certs, certFile);
        } else {
          CryptWriter.writeEncodedCertificates(certs, certFile);
        }
      } else {
        // Null cert chain indicates trusted cert entry
        // with single cert
        final Certificate cert = store.getCertificate(alias);
        if (certFile.getName().endsWith(PEM_SUFFIX)) {
          CryptWriter.writePemCertificate(cert, certFile);
        } else {
          CryptWriter.writeEncodedCertificate(cert, certFile);
        }
      }
      System.err.println("Wrote certificate to " + certFile);
      wroteData = true;
    }
    if (line.hasOption(OPT_KEY)) {
      final File keyFile = new File(line.getOptionValue(OPT_KEY));
      final PrivateKey key = (PrivateKey) store.getKey(
        alias,
        line.getOptionValue(OPT_PASS).toCharArray());
      if (keyFile.getName().endsWith(PEM_SUFFIX)) {
        CryptWriter.writePemKey(key, null, null, keyFile);
      } else {
        CryptWriter.writeEncodedKey(key, keyFile);
      }
      System.err.println("Wrote key to " + keyFile);
      wroteData = true;
    }
    if (!wroteData) {
      System.err.println(
        "No data was written because neither -cert nor -key was specified.");
    }
  }


  /** {@inheritDoc} */
  protected String getCommandName()
  {
    return COMMAND_NAME;
  }


  /**
   * Reads a keystore from disk using command line arguments.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @return  Initialized key store read from disk.
   *
   * @throws  Exception  On errors.
   */
  protected KeyStore readKeyStore(final CommandLine line)
    throws Exception
  {
    KeyStore store = null;
    if (line.hasOption(OPT_TYPE)) {
      store = CryptProvider.getKeyStore(line.getOptionValue(OPT_TYPE));
    } else {
      // Get a store of the default type
      store = CryptProvider.getKeyStore();
    }

    final File storeFile = new File(line.getOptionValue(OPT_STORE));
    final char[] passChars = line.getOptionValue(OPT_PASS).toCharArray();
    if (storeFile.exists()) {
      final InputStream in = new FileInputStream(storeFile);
      try {
        store.load(in, passChars);
      } finally {
        in.close();
      }
    } else {
      // An existing file is required for everything but import operation
      if (line.hasOption(OPT_IMPORT)) {
        // Initialize new keystore by passing null stream
        store.load(null, passChars);
      } else {
        throw new IllegalArgumentException(
          "Keystore does not exist at " + storeFile + ". " +
          "An existing keystore is required for this operation.");
      }
    }
    return store;
  }


  /**
   * Prints a string representation of the given certificate to STDOUT. For an
   * X.509 certificate, prints important fields.
   *
   * @param  cert  Certificate to print.
   *
   * @throws  Exception  On print errors.
   */
  protected void printCertificate(final Certificate cert)
    throws Exception
  {
    if (cert instanceof X509Certificate) {
      final X509Certificate xCert = (X509Certificate) cert;
      final byte[] encodedCert = xCert.getEncoded();
      System.out.println("Subject: " + xCert.getSubjectDN());
      System.out.println("Issuer: " + xCert.getIssuerDN());
      System.out.println(
        "Serial: " + hexConv.fromBytes(xCert.getSerialNumber().toByteArray()));
      System.out.println("Valid not before: " + xCert.getNotBefore());
      System.out.println("Valid not after: " + xCert.getNotAfter());
      System.out.println(
        "MD5 fingerprint: " + md5.digest(encodedCert, hexConv));
      System.out.println(
        "SHA1 fingerprint: " + sha1.digest(encodedCert, hexConv));
    } else {
      System.out.println(cert);
    }
  }


  /**
   * Validates the existence of required options for an operation.
   *
   * @param  line  Parsed command line arguments container.
   */
  protected void validateOptions(final CommandLine line)
  {
    if (!line.hasOption(OPT_STORE)) {
      throw new IllegalArgumentException("keystore option is required.");
    }
    if (!line.hasOption(OPT_PASS)) {
      throw new IllegalArgumentException("storepass option is required.");
    }
    if (line.hasOption(OPT_IMPORT) || line.hasOption(OPT_EXPORT)) {
      if (!line.hasOption(OPT_ALIAS)) {
        throw new IllegalArgumentException("alias option is required.");
      }
    }
    if (line.hasOption(OPT_IMPORT)) {
      if (!line.hasOption(OPT_CERT)) {
        throw new IllegalArgumentException("cert option is required.");
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy