//==============================================================================
// 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