Getting a File's MD5 Checksum in Java

Viewed 489206

I am looking to use Java to get the MD5 checksum of a file. I was really surprised but I haven't been able to find anything that shows how to get the MD5 checksum of a file.

How is it done?

21 Answers

There's an input stream decorator, java.security.DigestInputStream, so that you can compute the digest while using the input stream as you normally would, instead of having to make an extra pass over the data.

MessageDigest md = MessageDigest.getInstance("MD5");
try (InputStream is = Files.newInputStream(Paths.get("file.txt"));
     DigestInputStream dis = new DigestInputStream(is, md)) 
{
  /* Read decorated stream (dis) to EOF as normal... */
}
byte[] digest = md.digest();

There's an example at Real's Java-How-to using the MessageDigest class.

Check that page for examples using CRC32 and SHA-1 as well.

import java.io.*;
import java.security.MessageDigest;

public class MD5Checksum {

   public static byte[] createChecksum(String filename) throws Exception {
       InputStream fis =  new FileInputStream(filename);

       byte[] buffer = new byte[1024];
       MessageDigest complete = MessageDigest.getInstance("MD5");
       int numRead;

       do {
           numRead = fis.read(buffer);
           if (numRead > 0) {
               complete.update(buffer, 0, numRead);
           }
       } while (numRead != -1);

       fis.close();
       return complete.digest();
   }

   // see this How-to for a faster way to convert
   // a byte array to a HEX string
   public static String getMD5Checksum(String filename) throws Exception {
       byte[] b = createChecksum(filename);
       String result = "";

       for (int i=0; i < b.length; i++) {
           result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
       }
       return result;
   }

   public static void main(String args[]) {
       try {
           System.out.println(getMD5Checksum("apache-tomcat-5.5.17.exe"));
           // output :
           //  0bb2827c5eacf570b6064e24e0e6653b
           // ref :
           //  http://www.apache.org/dist/
           //          tomcat/tomcat-5/v5.5.17/bin
           //              /apache-tomcat-5.5.17.exe.MD5
           //  0bb2827c5eacf570b6064e24e0e6653b *apache-tomcat-5.5.17.exe
       }
       catch (Exception e) {
           e.printStackTrace();
       }
   }
}

The com.google.common.hash API offers:

  • A unified user-friendly API for all hash functions
  • Seedable 32- and 128-bit implementations of murmur3
  • md5(), sha1(), sha256(), sha512() adapters, change only one line of code to switch between these, and murmur.
  • goodFastHash(int bits), for when you don't care what algorithm you use
  • General utilities for HashCode instances, like combineOrdered / combineUnordered

Read the User Guide (IO Explained, Hashing Explained).

For your use-case Files.hash() computes and returns the digest value for a file.

For example a digest calculation (change SHA-1 to MD5 to get MD5 digest)

HashCode hc = Files.asByteSource(file).hash(Hashing.sha1());
"SHA-1: " + hc.toString();

Note that is much faster than , so use if you do not need a cryptographically secure checksum. Note also that should not be used to store passwords and the like since it is to easy to brute force, for passwords use , or instead.

For long term protection with hashes a Merkle signature scheme adds to the security and The Post Quantum Cryptography Study Group sponsored by the European Commission has recommended use of this cryptography for long term protection against quantum computers (ref).

Note that has a higher collision rate than the others.

I recently had to do this for just a dynamic string, MessageDigest can represent the hash in numerous ways. To get the signature of the file like you would get with the md5sum command I had to do something like the this:

try {
   String s = "TEST STRING";
   MessageDigest md5 = MessageDigest.getInstance("MD5");
   md5.update(s.getBytes(),0,s.length());
   String signature = new BigInteger(1,md5.digest()).toString(16);
   System.out.println("Signature: "+signature);

} catch (final NoSuchAlgorithmException e) {
   e.printStackTrace();
}

This obviously doesn't answer your question about how to do it specifically for a file, the above answer deals with that quiet nicely. I just spent a lot of time getting the sum to look like most application's display it, and thought you might run into the same trouble.

We were using code that resembles the code above in a previous post using

...
String signature = new BigInteger(1,md5.digest()).toString(16);
...

However, watch out for using BigInteger.toString() here, as it will truncate leading zeros... (for an example, try s = "27", checksum should be "02e74f10e0327ad868d138f2b4fdd6f0")

I second the suggestion to use Apache Commons Codec, I replaced our own code with that.

Here is a handy variation that makes use of InputStream.transferTo() from Java 9, and OutputStream.nullOutputStream() from Java 11. It requires no external libraries and does not need to load the entire file into memory.

public static String hashFile(String algorithm, File f) throws IOException, NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance(algorithm);

    try(BufferedInputStream in = new BufferedInputStream((new FileInputStream(f)));
        DigestOutputStream out = new DigestOutputStream(OutputStream.nullOutputStream(), md)) {
        in.transferTo(out);
    }

    String fx = "%0" + (md.getDigestLength()*2) + "x";
    return String.format(fx, new BigInteger(1, md.digest()));
}

and

hashFile("SHA-512", Path.of("src", "test", "resources", "some.txt").toFile());

returns

"e30fa2784ba15be37833d569280e2163c6f106506dfb9b07dde67a24bfb90da65c661110cf2c5c6f71185754ee5ae3fd83a5465c92f72abd888b03187229da29"
Related