//============================================================================== // tribble/security/StreamDigest.java //------------------------------------------------------------------------------ package tribble.security; // System imports import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; import java.lang.Exception; import java.lang.String; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; // Local imports // (None) /******************************************************************************* * Simple cryptographic file digest generator. * Reads an input stream and produces a cryptographic message digest from its * byte contents. * * @version $Revision: 1.3 $ $Date: 2003/03/22 14:34:01 $ * @since 2001-05-06 * @author * David R. Tribble, * david@tribble.com. *
* Copyright ©2001 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source code * provided that the original copyright and authorship notices remain * intact. */ public class StreamDigest { // Identification /** Revision information. */ static final String REV = "@(#)tribble/security/StreamDigest.java $Revision: 1.3 $ $Date: 2003/03/22 14:34:01 $\n"; /** This class name. */ public static final String CLASS_NAME = StreamDigest.class.getName(); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constants /** Default message digest algorithm ("MD5"). */ public final static String DFL_ALG = "MD5"; /** Input block size (in bytes). */ public final static int BLKSIZE = 64*1024; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public static methods /*************************************************************************** * Runs this class as a program. * * @param args * The command line arguments. * * @since 1.1, 2001-05-06 */ public static void main(String args[]) throws Exception { // Run this class run(args); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected variables /** Byte input stream. */ protected InputStream m_in; /** Message digest algorithm. */ protected String m_alg = DFL_ALG; /** Cryptographic message digest engine. */ protected MessageDigest m_dig; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constructors /*************************************************************************** * Default constructor. * * @since 1.1, 2001-05-06 */ public StreamDigest() { // Do nothing } /*************************************************************************** * Constructor. * Establishes the input stream. * * @param in * The input stream from which to read bytes. * * @since 1.1, 2001-05-06 */ public StreamDigest(InputStream in) { setInput(in); } /*************************************************************************** * Constructor. * Establishes the input stream and the cryptographic message digest * algorithm to use. * * @param in * The input stream from which to read bytes. * * @param alg * The cryptographic message digest algorithm to use. * * @throws NoSuchAlgorithmException * Thrown if alg does not specify a supported digest algorithm. * * @since 1.1, 2001-05-06 */ public StreamDigest(InputStream in, String alg) throws NoSuchAlgorithmException { setInput(in); setAlgorithm(alg); // May throw } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /*************************************************************************** * Establishes the cryptographic message digest algorithm to use. * * @param alg * The cryptographic message digest algorithm to use. * * @throws NoSuchAlgorithmException * Thrown if alg does not specify a supported digest algorithm. * * @since 1.1, 2001-05-06 */ public void setAlgorithm(String alg) throws NoSuchAlgorithmException { // Establish a new cryptographic message digest algorithm to use m_alg = alg; m_dig = MessageDigest.getInstance(alg); // May throw } /*************************************************************************** * Retrieves the cryptographic message digest algorithm to use. * * @return * The cryptographic message digest algorithm to use. * * @since 1.1, 2001-05-06 */ public String getAlgorithm() { // Retrieve the message digest algorithm return (m_alg); } /*************************************************************************** * Establishes the input stream from which to read bytes. * * @param in * The input stream from which to read bytes. * * @since 1.1, 2001-05-06 */ public void setInput(InputStream in) { // Establish a new input stream m_in = in; } /*************************************************************************** * Retrieves the input stream from which to read bytes. * * @return * The input stream from which to read bytes. * * @since 1.1, 2001-05-06 */ public InputStream getInput() { // Retrieve the input stream return (m_in); } /*************************************************************************** * Closes the input stream. * * @since 1.1, 2001-05-06 */ public void close() { // Close the input stream try { if (m_in != null) m_in.close(); } catch (IOException ex) { // Ignored } m_in = null; } /*************************************************************************** * Reads the contents of the input stream, producing a cryptographic message * digest, and returning it as a printable text string. * * @return * The resulting cryptographic message digest as a printable string of hex * digits. * * @throws IOException * Thrown if an I/O error occurs. * * @throws NoSuchAlgorithmException * Thrown if an unsupported digest algorithm is used. * * @since 1.1, 2001-05-06 */ public String digest() throws IOException, NoSuchAlgorithmException { byte[] digest; long nbytes; // Check that there is an input stream if (m_in == null) throw (new IOException("No input stream established")); // Ensure that there is a message digest algorithm and engine if (m_alg == null && m_dig == null) m_alg = DFL_ALG; if (m_dig == null) m_dig = MessageDigest.getInstance(m_alg); // May throw // Read the byte contents of the input stream and generate a digest { byte[] data; int len; // Allocate a byte input buffer data = new byte[BLKSIZE]; // Read and digest blocks of bytes from the input stream nbytes = 0; for (;;) { // Read the next block of bytes len = m_in.read(data); // May throw // Check for end of file if (len < 1) break; // Add the block of bytes to the message digest m_dig.update(data, 0, len); nbytes += len; } // Get the resulting message digest value digest = m_dig.digest(); if (digest == null) throw (new IOException("Can't retrieve message digest")); } // Convert the resulting digest into a hex string { char[] ch; int i, j; // Allocate a char array for the hex digits ch = new char[digest.length*2]; for (i = j = 0; i < digest.length; i++) { int b, d; // Get the hex digit pair for the next byte b = digest[i] & 0xFF; d = b >> 4; d = (d >= 0xA ? d-0xA+'A' : d+'0'); ch[j++] = (char) d; d = b & 0x0F; d = (d >= 0xA ? d-0xA+'A' : d+'0'); ch[j++] = (char) d; } // Return the hex digits in a string return (new String(ch, 0, j)); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected static methods /*************************************************************************** * Displays a program usage message, then punts. * *

* Reads the contents of one of more files, computing a cryptographic * message digest, and writes the resulting digest for each one to the * standard output. * * *

* Usage *

    *    java tribble.security.StreamDigest file...
    * 
* * If file is "-", input is read from the standard * input. * * @param args * The command line arguments. * * @since 1.1, 2001-05-06 */ protected static void usage() { // Display a program usage message System.out.println( "[" + CLASS_NAME + ".java " + "$Revision: 1.3 $ $Date: 2003/03/22 14:34:01 $" + "]"); System.out.println(); System.out.println( "Reads the contents of one of more files, computing a" + " cryptographic message"); System.out.println( "digest, and writes the resulting digest for each one to the" + " standard output."); System.out.println(); System.out.println( "usage: java " + CLASS_NAME + " file..."); System.out.println(); System.out.println( "If 'file' is \"-\", input is taken from the standard input."); // Punt System.exit(255); } /*************************************************************************** * Runs this class as a program. * *

* Reads the contents of one of more files, computing a cryptographic * message digest, and writes the resulting digest for each one to the * standard output. * *

* Usage
* See {@link #usage}. * * @param args * The command line arguments. * * @since 1.1, 2001-05-06 */ protected static void run(String args[]) throws IOException, NoSuchAlgorithmException { StreamDigest st; String dig; // Check args if (args == null || args.length < 1) usage(); // Create a message digest stream engine st = new StreamDigest(); // Read one or more input files, creating message digests for each for (int i = 0; i < args.length; i++) { try { InputStream in; // Open the next filename arg as an input stream if (args[i].equals("-")) in = System.in; else in = new FileInputStream(args[i]); // Compute a message digest from the input stream st.setInput(in); dig = st.digest(); // Display the message digest System.out.print(args[i]); System.out.println(":"); System.out.print(" "); System.out.print(dig); System.out.println(" (" + st.getAlgorithm() + ")"); // Clean up if (in != System.in) st.close(); } catch (FileNotFoundException ex) { System.err.println("Can't open/read \"" + args[i] + "\""); } } } } // End StreamDigest.java