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

edu.vt.middleware.crypt.symmetric.SymmetricCli Maven / Gradle / Ivy

/*
  $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.symmetric;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import edu.vt.middleware.crypt.AbstractEncryptionCli;
import edu.vt.middleware.crypt.CryptException;
import edu.vt.middleware.crypt.digest.DigestAlgorithm;
import edu.vt.middleware.crypt.pbe.EncryptionScheme;
import edu.vt.middleware.crypt.pbe.KeyGenerator;
import edu.vt.middleware.crypt.pbe.OpenSSLEncryptionScheme;
import edu.vt.middleware.crypt.pbe.OpenSSLKeyGenerator;
import edu.vt.middleware.crypt.pbe.PBES1EncryptionScheme;
import edu.vt.middleware.crypt.pbe.PBES2EncryptionScheme;
import edu.vt.middleware.crypt.pbe.PBKDF1KeyGenerator;
import edu.vt.middleware.crypt.pbe.PBKDF2KeyGenerator;
import edu.vt.middleware.crypt.pbe.PKCS12EncryptionScheme;
import edu.vt.middleware.crypt.pbe.PKCS12KeyGenerator;
import edu.vt.middleware.crypt.pkcs.PBEParameter;
import edu.vt.middleware.crypt.pkcs.PBKDF2Parameters;
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 symmetric encryption operations.
 *
 * @author  Middleware Services
 * @version  $Revision: 12 $
 */
public class SymmetricCli extends AbstractEncryptionCli
{

  /** Cipher mode option. */
  protected static final String OPT_MODE = "mode";

  /** Cipher padding option. */
  protected static final String OPT_PADDING = "padding";

  /** Cipher initialization vector option. */
  protected static final String OPT_IV = "iv";

  /** Cipher key option. */
  protected static final String OPT_KEY = "key";

  /** Cipher key size. */
  protected static final String OPT_KEYSIZE = "keysize";

  /** Trigger password-based encryption key generation. */
  protected static final String OPT_PBE = "pbe";

  /** PBE key generation strategy, e.g. PKCS5S1 */
  protected static final String OPT_SCHEME = "scheme";

  /** Digest algorithm used with PBE modes that allow it. */
  protected static final String OPT_DIGEST = "digest";

  /** Salt for PBE key generation. */
  protected static final String OPT_SALT = "salt";

  /** Iteration count for PBE key generation. */
  protected static final String OPT_ITERATIONS = "iter";

  /** Generate key option. */
  protected static final String OPT_GENKEY = "genkey";

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


  /** Converts hex to bytes and vice versa. */
  private HexConverter hexConv = new HexConverter();


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


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

    final Option mode = new Option(OPT_MODE, true, "cipher mode, e.g. CBC");
    mode.setArgName("name");
    mode.setOptionalArg(false);

    final Option padding = new Option(
      OPT_PADDING,
      true,
      "cipher padding strategy, e.g. PKCS5Padding");
    padding.setArgName("padding");
    padding.setOptionalArg(false);

    final Option key = new Option(OPT_KEY, true, "encryption/decryption key");
    key.setArgName("filepath");
    key.setOptionalArg(false);

    final Option keySize = new Option(
      OPT_KEYSIZE,
      true,
      "key size in bits; only needed if -key option is not specified");
    keySize.setArgName("bits");
    keySize.setOptionalArg(false);

    final Option iv = new Option(OPT_IV, true, "initialization vectory in hex");
    iv.setArgName("hex_iv");
    iv.setOptionalArg(false);

    final Option pbe = new Option(
      OPT_PBE,
      true,
      "generate PBE key from password/phrase; uses pkcs5s2 by default");
    pbe.setArgName("password");
    pbe.setOptionalArg(false);

    final Option pbeScheme = new Option(
      OPT_SCHEME,
      true,
      "PBE key generation mode; one of pkcs5s1, pkcs5s2, openssl, pkcs12");
    pbeScheme.setArgName("name");
    pbeScheme.setOptionalArg(false);

    final Option pbeDigest = new Option(
      OPT_DIGEST,
      true,
      "digest algorithm to use with PBE mode pkcs5s1 or pkcs12");
    pbeDigest.setArgName("name");
    pbeDigest.setOptionalArg(false);

    final Option salt = new Option(
      OPT_SALT,
      true,
      "salt for PBE key generation in hex");
    salt.setArgName("hex_salt");
    salt.setOptionalArg(false);

    final Option iterations = new Option(
      OPT_ITERATIONS,
      true,
      "iteration count for PBE key generation");
    salt.setArgName("count");
    salt.setOptionalArg(false);

    options.addOption(mode);
    options.addOption(padding);
    options.addOption(key);
    options.addOption(keySize);
    options.addOption(iv);
    options.addOption(pbe);
    options.addOption(pbeScheme);
    options.addOption(pbeDigest);
    options.addOption(salt);
    options.addOption(iterations);
    options.addOption(new Option(OPT_GENKEY, "generate new encryption key"));
    options.addOption(new Option(OPT_ENCRYPT, "perform encryption"));
    options.addOption(new Option(OPT_DECRYPT, "perform decryption"));
  }


  /** {@inheritDoc} */
  protected void dispatch(final CommandLine line)
    throws Exception
  {
    if (line.hasOption(OPT_ENCRYPT)) {
      encrypt(line);
    } else if (line.hasOption(OPT_DECRYPT)) {
      decrypt(line);
    } else if (line.hasOption(OPT_GENKEY)) {
      genKey(line);
    } else {
      printHelp();
    }
  }


  /**
   * Creates a new symmetric encryption algorithm instance based on CLI options.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @return  New instance of an initialized symmetric algorithm.
   */
  protected SymmetricAlgorithm newAlgorithm(final CommandLine line)
  {
    final String algName = line.getOptionValue(OPT_CIPHER);
    SymmetricAlgorithm algorithm = null;
    if (line.hasOption(OPT_MODE)) {
      if (line.hasOption(OPT_PADDING)) {
        algorithm = SymmetricAlgorithm.newInstance(
          algName,
          line.getOptionValue(OPT_MODE),
          line.getOptionValue(OPT_PADDING));
      } else {
        algorithm = SymmetricAlgorithm.newInstance(
          algName,
          line.getOptionValue(OPT_MODE),
          SymmetricAlgorithm.DEFAULT_PADDING);
      }
    } else if (line.hasOption(OPT_PADDING)) {
      algorithm = SymmetricAlgorithm.newInstance(
        algName,
        SymmetricAlgorithm.DEFAULT_MODE,
        line.getOptionValue(OPT_PADDING));
    } else {
      algorithm = SymmetricAlgorithm.newInstance(algName);
    }
    return algorithm;
  }


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


  /**
   * Perform an encryption operation using data specified on the command line.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On encryption errors.
   */
  protected void encrypt(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final SymmetricAlgorithm alg = newAlgorithm(line);
    if (line.hasOption(OPT_KEY)) {
      alg.setKey(readKey(line));
      if (line.hasOption(OPT_IV)) {
        alg.setIV(hexConv.toBytes(line.getOptionValue(OPT_IV)));
      }
      encrypt(alg, getInputStream(line), getOutputStream(line));
    } else if (line.hasOption(OPT_PBE)) {
      final InputStream in = getInputStream(line);
      final OutputStream out = getOutputStream(line);
      try {
        getPBEScheme(alg, line).encrypt(
          line.getOptionValue(OPT_PBE).toCharArray(),
          in,
          out);
      } finally {
        closeStream(in);
        closeStream(out);
      }
    } else {
      throw new IllegalArgumentException(
        "Either -key or -pbe is required for encryption or decryption.");
    }
  }


  /**
   * Perform a decryption operation using data specified on the command line.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On decryption errors.
   */
  protected void decrypt(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final SymmetricAlgorithm alg = newAlgorithm(line);
    if (line.hasOption(OPT_KEY)) {
      alg.setKey(readKey(line));
      if (line.hasOption(OPT_IV)) {
        alg.setIV(hexConv.toBytes(line.getOptionValue(OPT_IV)));
      }
      decrypt(alg, getInputStream(line), getOutputStream(line));
    } else if (line.hasOption(OPT_PBE)) {
      final InputStream in = getInputStream(line);
      final OutputStream out = getOutputStream(line);
      try {
        getPBEScheme(alg, line).decrypt(
          line.getOptionValue(OPT_PBE).toCharArray(),
          in,
          out);
      } finally {
        closeStream(in);
        closeStream(out);
      }
    } else {
      throw new IllegalArgumentException(
        "Either -key or -pbe is required for encryption or decryption.");
    }
  }


  /**
   * Generate a new encryption key to using command line arguments.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On key generation errors.
   */
  protected void genKey(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final SymmetricAlgorithm alg = newAlgorithm(line);
    SecretKey key = null;
    if (line.hasOption(OPT_PBE)) {
      key = generatePBEKey(alg, line);
    } else {
      if (line.hasOption(OPT_KEYSIZE)) {
        final int size = Integer.parseInt(line.getOptionValue(OPT_KEYSIZE));
        System.err.println("Generating key of size " + size);
        key = alg.generateKey(size);
      } else {
        System.err.println("Generating key of default size for " + alg);
        key = alg.generateKey();
      }
    }
    CryptWriter.writeEncodedKey(key, getOutputStream(line));
    if (line.hasOption(OPT_OUTFILE)) {
      System.err.println("Wrote key to " + line.getOptionValue(OPT_OUTFILE));
    }
  }


  /**
   * Generates a PBE key from command line options including a password.
   *
   * @param  alg  Symmetric algorithm for which a compatible key should be
   * generated.
   * @param  line  Parsed command line arguments container.
   *
   * @return  Secret key from password.
   *
   * @throws  Exception  On key generation errors.
   */
  protected SecretKey generatePBEKey(
    final SymmetricAlgorithm alg,
    final CommandLine line)
    throws Exception
  {
    if (!line.hasOption(OPT_SALT)) {
      throw new IllegalArgumentException(
        "Salt is required for PBE key generation.");
    }
    if (!line.hasOption(OPT_ITERATIONS)) {
      throw new IllegalArgumentException(
        "Iteration count is required for PBE key generation.");
    }

    DigestAlgorithm digest = null;
    if (line.hasOption(OPT_DIGEST)) {
      digest = DigestAlgorithm.newInstance(line.getOptionValue(OPT_DIGEST));
    }

    String pbeScheme = null;
    if (line.hasOption(OPT_SCHEME)) {
      pbeScheme = line.getOptionValue(OPT_SCHEME).toLowerCase();
    }

    final char[] pass = line.getOptionValue(OPT_PBE).toCharArray();
    final byte[] salt = hexConv.toBytes(line.getOptionValue(OPT_SALT));
    final int iterations = Integer.parseInt(
      line.getOptionValue(OPT_ITERATIONS));
    final int keySize = line.hasOption(OPT_KEYSIZE)
      ? Integer.parseInt(line.getOptionValue(OPT_KEYSIZE)) : -1;
    final KeyGenerator generator;
    final byte[] derivedKey;
    final byte[] derivedIV;
    if ("pkcs12".equals(pbeScheme)) {
      if (digest == null) {
        throw new IllegalArgumentException("pkcs12 requires a digest.");
      }
      if (keySize < 0) {
        throw new IllegalArgumentException(
          "Key size is required for pkcs5s2 PBE key generation.");
      }
      System.err.println("Generating PKCS#12 PBE key.");
      generator = new PKCS12KeyGenerator(digest, salt, iterations);
      derivedKey = generator.generate(pass, keySize);
      derivedIV = generator.generate(pass, alg.getBlockSize() * 8);
    } else if ("pkcs5s1".equals(pbeScheme)) {
      if (digest == null) {
        throw new IllegalArgumentException("pkcs5s1 requires a digest.");
      }
      System.err.println("Generating PKCS#5 PBE key using PBKDF1 scheme.");
      generator = new PBKDF1KeyGenerator(digest, salt, iterations);

      final byte[] keyWithIV = generator.generate(pass, 128);
      derivedKey = new byte[8];
      derivedIV = new byte[8];
      System.arraycopy(keyWithIV, 0, derivedKey, 0, 8);
      System.arraycopy(keyWithIV, 8, derivedIV, 0, 16);
    } else if ("openssl".equals(pbeScheme)) {
      if (keySize < 0) {
        throw new IllegalArgumentException(
          "Key size is required for pkcs5s2 PBE key generation.");
      }
      System.err.println("Generating OpenSSL PBE key.");
      generator = new OpenSSLKeyGenerator(salt);
      derivedKey = generator.generate(pass, keySize);
      derivedIV = generator.generate(pass, alg.getBlockSize() * 8);
    } else {
      // Default is pkcs5s2
      if (digest != null) {
        System.err.println("Ignoring digest for pkcs5s2 PBE scheme.");
      }
      if (keySize < 0) {
        throw new IllegalArgumentException(
          "Key size is required for pkcs5s2 PBE key generation.");
      }
      System.err.println("Generating PKCS#5 PBE key using PBKDF2 scheme.");
      generator = new PBKDF2KeyGenerator(salt, iterations);
      derivedKey = generator.generate(pass, keySize);
      derivedIV = generator.generate(pass, alg.getBlockSize() * 8);
    }
    System.err.println("Derived key: " + hexConv.fromBytes(derivedKey));
    System.err.println("Derived iv: " + hexConv.fromBytes(derivedIV));
    return new SecretKeySpec(derivedKey, alg.getAlgorithm());
  }


  /**
   * Creates a symmetric key from a file defined by CLI arguments.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @return  Symmetric encryption/decryption key.
   *
   * @throws  CryptException  On cryptographic errors.
   * @throws  IOException  On IO errors.
   */
  protected SecretKey readKey(final CommandLine line)
    throws CryptException, IOException
  {
    return
      CryptReader.readSecretKey(
        new File(line.getOptionValue(OPT_KEY)),
        line.getOptionValue(OPT_CIPHER));
  }


  /**
   * 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_CIPHER)) {
      throw new IllegalArgumentException("cipher option is required.");
    }
  }


  /**
   * Gets a password-based encryption scheme based on command line arguments.
   *
   * @param  alg  Symmetric cipher algorithm.
   * @param  line  parsed command line arguments container.
   *
   * @return  Initialized encryption scheme.
   */
  protected EncryptionScheme getPBEScheme(
    final SymmetricAlgorithm alg,
    final CommandLine line)
  {
    if (!line.hasOption(OPT_SALT)) {
      throw new IllegalArgumentException(
        "Salt is required for PBE encryption/decryption.");
    }
    if (!line.hasOption(OPT_ITERATIONS)) {
      throw new IllegalArgumentException(
        "Iteration count is required for PBE encryption/decryption.");
    }

    DigestAlgorithm digest = null;
    if (line.hasOption(OPT_DIGEST)) {
      digest = DigestAlgorithm.newInstance(line.getOptionValue(OPT_DIGEST));
    }

    String scheme = null;
    if (line.hasOption(OPT_SCHEME)) {
      scheme = line.getOptionValue(OPT_SCHEME).toLowerCase();
    }

    final byte[] salt = hexConv.toBytes(line.getOptionValue(OPT_SALT));
    final int iterations = Integer.parseInt(
      line.getOptionValue(OPT_ITERATIONS));
    final int keySize = line.hasOption(OPT_KEYSIZE)
      ? Integer.parseInt(line.getOptionValue(OPT_KEYSIZE)) : 0;
    final EncryptionScheme pbeScheme;
    if ("pkcs12".equals(scheme)) {
      if (digest == null) {
        throw new IllegalArgumentException("pkcs12 requires a digest.");
      }
      if (keySize < 0) {
        throw new IllegalArgumentException(
          "Key size is required for pkcs5s2 PBE key generation.");
      }
      System.err.println("Using PKCS#12 PBE encryption scheme.");
      pbeScheme = new PKCS12EncryptionScheme(
        alg,
        digest,
        new PBEParameter(salt, iterations),
        keySize);
    } else if ("pkcs5s1".equals(scheme)) {
      if (digest == null) {
        throw new IllegalArgumentException("pkcs12 requires a digest.");
      }
      System.err.println("Using PKCS#5 PBES1 encryption scheme.");
      pbeScheme = new PBES1EncryptionScheme(
        alg,
        digest,
        new PBEParameter(salt, iterations));
    } else if ("openssl".equals(scheme)) {
      if (keySize < 0) {
        throw new IllegalArgumentException(
          "Key size is required for pkcs5s2 PBE key generation.");
      }
      System.err.println("Using OpenSSL encryption scheme.");
      pbeScheme = new OpenSSLEncryptionScheme(alg, salt, keySize);
    } else {
      // Default is pkcs5s2
      if (digest != null) {
        System.err.println("Ignoring digest for pkcs5s2 PBE scheme.");
      }
      if (keySize < 0) {
        throw new IllegalArgumentException(
          "Key size is required for pkcs5s2 PBE key generation.");
      }
      System.err.println("Using PKCS#5 PBES2 encryption scheme.");
      pbeScheme = new PBES2EncryptionScheme(
        alg,
        new PBKDF2Parameters(salt, iterations, keySize / 8));
    }
    if (line.hasOption(OPT_IV)) {
      System.err.println("Using provided IV instead of generated value.");
      alg.setIV(hexConv.toBytes(line.getOptionValue(OPT_IV)));
    }
    return pbeScheme;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy