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

edu.vt.middleware.crypt.signature.SignatureCli 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.signature;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import edu.vt.middleware.crypt.AbstractCli;
import edu.vt.middleware.crypt.CryptException;
import edu.vt.middleware.crypt.io.Base64FilterInputStream;
import edu.vt.middleware.crypt.io.HexFilterInputStream;
import edu.vt.middleware.crypt.util.Base64Converter;
import edu.vt.middleware.crypt.util.Converter;
import edu.vt.middleware.crypt.util.CryptReader;
import edu.vt.middleware.crypt.util.HexConverter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;

/**
 * Command line interface for cryptographic signature operations.
 *
 * @author  Middleware Services
 * @version  $Revision: 12 $
 */
public class SignatureCli extends AbstractCli
{

  /** Signature algorithm option. */
  protected static final String OPT_ALG = "alg";

  /** Output encoding format. */
  protected static final String OPT_ENCODING = "encoding";

  /** Message digest used to produce encoded message to sign. */
  protected static final String OPT_DIGEST = "digest";

  /** Path to key used for signing or verification. */
  protected static final String OPT_KEY = "key";

  /** Sign operation option. */
  protected static final String OPT_SIGN = "sign";

  /** Verify operation option. */
  protected static final String OPT_VERIFY = "verify";

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


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


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

    final Option alg = new Option(
      OPT_ALG,
      true,
      "signature algorithm; either DSA or RSA");
    alg.setArgName("name");
    alg.setOptionalArg(false);

    final Option key = new Option(
      OPT_KEY,
      true,
      "DER-encoded PKCS#8 private key for signing or " +
      "X.509 cert/public key for verification");
    key.setArgName("filepath");
    key.setOptionalArg(false);

    final Option infile = new Option(
      OPT_INFILE,
      true,
      "file to sign/verify; defaults to STDIN");
    infile.setArgName("filepath");
    infile.setOptionalArg(false);

    final Option digest = new Option(
      OPT_DIGEST,
      true,
      "message digest algorithm used to produce encoded message to sign");
    digest.setArgName("algname");
    digest.setOptionalArg(false);

    final Option encoding = new Option(
      OPT_ENCODING,
      true,
      "signature encoding format, either base64 or hex");
    encoding.setArgName("format");
    encoding.setOptionalArg(false);

    final Option verify = new Option(
      OPT_VERIFY,
      true,
      "verify signature in given file; " +
      "signature encoding determined by -encoding option");
    encoding.setArgName("sigfilepath");
    encoding.setOptionalArg(false);

    options.addOption(alg);
    options.addOption(key);
    options.addOption(infile);
    options.addOption(digest);
    options.addOption(encoding);
    options.addOption(verify);
    options.addOption(new Option(OPT_SIGN, "perform sign operation"));
  }


  /** {@inheritDoc} */
  protected void dispatch(final CommandLine line)
    throws Exception
  {
    if (line.hasOption(OPT_SIGN)) {
      sign(line);
    } else if (line.hasOption(OPT_VERIFY)) {
      verify(line);
    } else {
      printHelp();
    }
  }


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


  /**
   * Creates a new signature algorithm instance based on command line args.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @return  New instance from CLI args.
   */
  protected SignatureAlgorithm newInstance(final CommandLine line)
  {
    SignatureAlgorithm sig = null;
    if (line.hasOption(OPT_DIGEST)) {
      sig = SignatureAlgorithm.newInstance(
        line.getOptionValue(OPT_ALG),
        line.getOptionValue(OPT_DIGEST));
    } else {
      sig = SignatureAlgorithm.newInstance(line.getOptionValue(OPT_ALG));
    }
    return sig;
  }


  /**
   * Perform a signature operation on input data.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On sign errors.
   */
  protected void sign(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final SignatureAlgorithm sig = newInstance(line);
    final File keyFile = new File(line.getOptionValue(OPT_KEY));
    System.err.println("Reading private key from " + keyFile);
    sig.setSignKey(readPrivateKey(line));
    sig.initSign();

    final InputStream in = getInputStream(line);
    final byte[] sigBytes = sig.sign(getInputStream(line));
    closeStream(in);
    if (line.hasOption(OPT_ENCODING)) {
      final String encName = line.getOptionValue(OPT_ENCODING);
      Converter conv = null;
      if (BASE_64_ENCODING.equals(encName)) {
        conv = new Base64Converter();
      } else if (HEX_ENCODING.equals(encName)) {
        conv = new HexConverter();
      } else {
        throw new IllegalArgumentException("Unknown encoding.");
      }
      System.out.println(conv.fromBytes(sigBytes));
    } else {
      // Suppress line feed since the expected use case here is chaining
      // with other tools
      System.out.print(sigBytes);
    }
  }


  /**
   * Perform a verification operation on input data.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @throws  Exception  On sign errors.
   */
  protected void verify(final CommandLine line)
    throws Exception
  {
    validateOptions(line);

    final InputStream in = getInputStream(line);
    final SignatureAlgorithm sig = newInstance(line);
    sig.setVerifyKey(readPublicKey(line));
    sig.initVerify();

    boolean isVerified = false;
    try {
      isVerified = sig.verify(in, readSignature(line));
    } finally {
      closeStream(in);
    }
    if (isVerified) {
      System.out.println("SUCCESS -- signature verified.");
    } else {
      System.out.println("FAILURE -- signature does not match.");
    }
  }


  /**
   * Creates a public key from a file defined by CLI arguments.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @return  Public key used for signature verification.
   *
   * @throws  Exception  On IO or key format errors.
   */
  protected PublicKey readPublicKey(final CommandLine line)
    throws Exception
  {
    PublicKey key = null;
    final File keyFile = new File(line.getOptionValue(OPT_KEY));
    System.err.println("Reading public key from " + keyFile);
    try {
      // The commonest case is an X.509 cert containing the public key
      key = CryptReader.readCertificate(keyFile).getPublicKey();
    } catch (Exception e) {
      // Try treating file as a standalone key in X.509 format
      key = CryptReader.readPublicKey(keyFile);
    }
    return key;
  }


  /**
   * Creates a private key from a file defined by CLI arguments.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @return  Private key.
   *
   * @throws  CryptException  On crypto errors.
   * @throws  IOException  On I/O errors.
   */
  protected PrivateKey readPrivateKey(final CommandLine line)
    throws CryptException, IOException
  {
    final File keyFile = new File(line.getOptionValue(OPT_KEY));
    return CryptReader.readPrivateKey(keyFile);
  }


  /**
   * Reads a cryptographic signature from a file, possibly in encoded format,
   * and returns the result as the raw signature bytes.
   *
   * @param  line  Parsed command line arguments container.
   *
   * @return  Signature bytes.
   *
   * @throws  IOException  On read errors.
   */
  protected byte[] readSignature(final CommandLine line)
    throws IOException
  {
    InputStream in = getInputStream(line, OPT_VERIFY);
    if (line.hasOption(OPT_ENCODING)) {
      final String encName = line.getOptionValue(OPT_ENCODING);
      if (BASE_64_ENCODING.equals(encName)) {
        in = new Base64FilterInputStream(in);
      } else if (HEX_ENCODING.equals(encName)) {
        in = new HexFilterInputStream(in);
      } else {
        throw new IllegalArgumentException("Unknown encoding.");
      }
    }

    final ByteArrayOutputStream os = new ByteArrayOutputStream();
    final int bufSize = 1024;
    final byte[] buffer = new byte[bufSize];
    int count = 0;
    while ((count = in.read(buffer)) > 0) {
      os.write(buffer, 0, count);
    }
    return os.toByteArray();
  }


  /**
   * 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_ALG)) {
      throw new IllegalArgumentException("alg option is required.");
    }
    if (!line.hasOption(OPT_KEY)) {
      throw new IllegalArgumentException("key option is required.");
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy