dk.netarkivet.common.utils.ChecksumCalculator.java Source code

Java tutorial

Introduction

Here is the source code for dk.netarkivet.common.utils.ChecksumCalculator.java

Source

/*
 * #%L
 * Netarchivesuite - common
 * %%
 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
 *             the National Library of France and the Austrian National Library.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */
package dk.netarkivet.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.io.IOUtils;

import dk.netarkivet.common.Constants;
import dk.netarkivet.common.exceptions.ArgumentNotValid;
import dk.netarkivet.common.exceptions.IOFailure;
import dk.netarkivet.common.exceptions.IllegalState;

/**
 * Calculates MD5 or SHA1 checksums on files using the built-in Java methods.
 */
public final class ChecksumCalculator {

    /** Defines the MD5 checksum algorithm */
    public static final String MD5 = "MD5";
    /** Defines the SHA1 checksum algorithm */
    public static final String SHA1 = "SHA1";

    /**
     * Calculate MD5 for a file.
     *
     * @param src The file to calculate MD5 for.
     * @return The MD5 sum of a file as a 32 characters long Hex string.
     */
    public static String calculateMd5(final File src) {
        ArgumentNotValid.checkNotNull(src, "File src");
        ArgumentNotValid.checkTrue(src.isFile(), "Argument should be a file");
        // Get the MD5 and return it
        try {
            final FileInputStream fileInputStream = new FileInputStream(src);
            try {
                return calculateMd5(fileInputStream);
            } finally {
                IOUtils.closeQuietly(fileInputStream);
            }
        } catch (FileNotFoundException e) {
            throw new IOFailure("Could not read file '" + src.getAbsolutePath() + "'", e);
        }
    }

    /**
     * Calculate the SHA-1 DIGEST for a file.
     *
     * @param src The file to calculate SHA-1 for.
     * @return The SHA-1 sum of a file as a 32 characters long Hex string.
     */
    public static String calculateSha1(final File src) {
        ArgumentNotValid.checkNotNull(src, "File src");
        ArgumentNotValid.checkTrue(src.isFile(), "Argument should be a file");
        // Get the SHA-1 digest and return it
        try {
            final FileInputStream fileInputStream = new FileInputStream(src);
            try {
                return calculateSha1(fileInputStream);
            } finally {
                IOUtils.closeQuietly(fileInputStream);
            }
        } catch (FileNotFoundException e) {
            throw new IOFailure("Could not read file '" + src.getAbsolutePath() + "'", e);
        }
    }

    /**
     * Calculates an MD5 digest on an InputStream, throwing away the data itself. Throws Alert if there is an error
     * reading from the stream
     *
     * @param instream An <code>InputStream</code> to calculate the MD5 digest on. The contents of the stream will be
     * consumed by this call, but the stream will not be closed.
     * @return The calculated MD5 digest as a string.
     */
    public static String calculateMd5(final InputStream instream) {
        return calculateDigest(instream, MD5);
    }

    /**
     * Calculates an SHA-1 digest on an InputStream, throwing away the data itself. Throws Alert if there is an error
     * reading from the stream
     *
     * @param instream An <code>InputStream</code> to calculate the SHA-1 digest on. The contents of the stream will be
     * consumed by this call, but the stream will not be closed.
     * @return The calculated SHA-1 digest as a string.
     */
    public static String calculateSha1(final InputStream instream) {
        return calculateDigest(instream, SHA1);
    }

    /**
     * Generate an MD5 for a byte array.
     *
     * @param msg The given bytearray
     * @return the MD5 for a byte array
     */
    public static String calculateMd5(final byte[] msg) {
        return toHex(getMessageDigest(MD5).digest(msg));
    }

    /**
     * Calculates a digest on an InputStream, throwing away the data itself. Throws Alert if there is an error reading
     * from the stream
     *
     * @param instream An <code>InputStream</code> to calculate the digest on. The contents of the stream will be
     * consumed by this call, but the stream will not be closed.
     * @param algorithm digest algorithm to use
     * @return The calculated digest as a string.
     */
    private static String calculateDigest(final InputStream instream, final String algorithm) {
        final byte[] buffer = new byte[Constants.IO_BUFFER_SIZE];
        final MessageDigest messageDigest = getMessageDigest(algorithm);
        messageDigest.reset();
        int bytesRead;
        try {
            while ((bytesRead = instream.read(buffer)) != -1) {
                messageDigest.update(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new IOFailure("Error making a '" + algorithm + "' digest on the inputstream", e);
        }
        return toHex(messageDigest.digest());
    }

    private static final char[] hexdigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
            'e', 'f' };

    /**
     * Converts a byte array to a hexstring.
     *
     * @param ba the bytearray to be converted
     * @return ba converted to a hexstring
     */
    public static String toHex(final byte[] ba) {
        int baLen = ba.length;
        char[] hexchars = new char[baLen * 2];
        int cIdx = 0;
        for (int i = 0; i < baLen; ++i) {
            hexchars[cIdx++] = hexdigit[(ba[i] >> 4) & 0x0F];
            hexchars[cIdx++] = hexdigit[ba[i] & 0x0F];
        }
        return new String(hexchars);
    }

    public static byte[] digestFile(File src, String digestAlgorithm) {
        ArgumentNotValid.checkNotNull(src, "File src");
        ArgumentNotValid.checkTrue(src.isFile(), "Argument should be a file");
        try {
            FileInputStream fileInputStream = new FileInputStream(src);
            try {
                return digestInputStream(fileInputStream, digestAlgorithm);
            } finally {
                IOUtils.closeQuietly(fileInputStream);
            }
        } catch (FileNotFoundException e) {
            throw new IOFailure("Could not read file '" + src.getAbsolutePath() + "'", e);
        }
    }

    public static byte[] digestInputStream(InputStream instream, String algorithm) {
        byte[] buffer = new byte[Constants.IO_BUFFER_SIZE];
        MessageDigest messageDigest = getMessageDigest(algorithm);
        messageDigest.reset();
        int bytesRead;
        try {
            while ((bytesRead = instream.read(buffer)) != -1) {
                messageDigest.update(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new IOFailure("Error making a '" + algorithm + "' digest on the inputstream", e);
        }
        return messageDigest.digest();
    }

    /**
     * Get a MessageDigest for a specific algorithm.
     *
     * @param algorithm a specific MessageDigest algorithm.
     * @return a MessageDigest for a specific algorithm
     */
    public static MessageDigest getMessageDigest(final String algorithm) {
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalState("The '" + algorithm + "' algorithm is not available", e);
        }
        return messageDigest;
    }

}