73

In my app I have a requirement to generate an MD5 checksum for a file. Could you please tell me if there is any way in which this can be achieved?

Thank you.

Ingrid Cooper
  • 1,191
  • 3
  • 16
  • 27

16 Answers16

158

This code is from the CMupdater, from the CyanogenMod 10.2 android ROM. It tests the downloaded ROMs into the updater App.

code: https://github.com/CyanogenMod/android_packages_apps_CMUpdater/blob/cm-10.2/src/com/cyanogenmod/updater/utils/MD5.java

It works like a charm:

/*
 * Copyright (C) 2012 The CyanogenMod Project
 *
 * * Licensed under the GNU GPLv2 license
 *
 * The text of the license can be found in the LICENSE file
 * or at https://www.gnu.org/licenses/gpl-2.0.txt
 */

package com.cyanogenmod.updater.utils;

import android.text.TextUtils;
import android.util.Log;

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

public class MD5 {
    private static final String TAG = "MD5";

    public static boolean checkMD5(String md5, File updateFile) {
        if (TextUtils.isEmpty(md5) || updateFile == null) {
            Log.e(TAG, "MD5 string empty or updateFile null");
            return false;
        }

        String calculatedDigest = calculateMD5(updateFile);
        if (calculatedDigest == null) {
            Log.e(TAG, "calculatedDigest null");
            return false;
        }

        Log.v(TAG, "Calculated digest: " + calculatedDigest);
        Log.v(TAG, "Provided digest: " + md5);

        return calculatedDigest.equalsIgnoreCase(md5);
    }

    public static String calculateMD5(File updateFile) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "Exception while getting digest", e);
            return null;
        }

        InputStream is;
        try {
            is = new FileInputStream(updateFile);
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Exception while getting FileInputStream", e);
            return null;
        }

        byte[] buffer = new byte[8192];
        int read;
        try {
            while ((read = is.read(buffer)) > 0) {
                digest.update(buffer, 0, read);
            }
            byte[] md5sum = digest.digest();
            BigInteger bigInt = new BigInteger(1, md5sum);
            String output = bigInt.toString(16);
            // Fill to 32 chars
            output = String.format("%32s", output).replace(' ', '0');
            return output;
        } catch (IOException e) {
            throw new RuntimeException("Unable to process file for MD5", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                Log.e(TAG, "Exception on closing MD5 input stream", e);
            }
        }
    }
}
dentex
  • 3,223
  • 4
  • 28
  • 47
  • 3
    You're welcome. And yes, GPL. It propagates to the whole app. – dentex Feb 07 '14 at 08:14
  • 8
    Useless because of GPL – Mensly May 01 '19 at 23:30
  • Is there some explanation as to what is going on in this piece of code ? Would be really helpful. – iammrmehul Jun 21 '19 at 08:49
  • @Mensly, all subscriber content is CC-BY-SA. See [the Terms of Service](/legal/terms-of-service) and click the share button under this answer's content. You'll see the CC-BY-SA license. dentex is dual-licensing here. – starball Jul 14 '23 at 22:54
44

Convert the file content into string & use the below method:

public static String getMD5EncryptedString(String encTarget){
        MessageDigest mdEnc = null;
        try {
            mdEnc = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Exception while encrypting to md5");
            e.printStackTrace();
        } // Encryption algorithm
        mdEnc.update(encTarget.getBytes(), 0, encTarget.length());
        String md5 = new BigInteger(1, mdEnc.digest()).toString(16);
        while ( md5.length() < 32 ) {
            md5 = "0"+md5;
        }
        return md5;
    }

Note that this simple approach is suitable for smallish strings, but will not be efficient for large files. For the latter, see dentex's answer.

Community
  • 1
  • 1
hemu
  • 3,199
  • 3
  • 44
  • 66
  • 3
    Beware that before Android 4.2.1 `MessageDigest.getInstance()` was not thread safe. Check [bug report](https://code.google.com/p/android/issues/detail?id=37937) and [fix](https://android-review.googlesource.com/#/c/40145/). So if you're using `HttpResponseCache` you better check for something different – mente Dec 27 '13 at 10:46
  • this method is removing initial 0 from final md5 sting:(. how to overcome this ? – Uniruddh Jan 09 '14 at 13:40
  • 44
    I came here to MD5 a 700MB file, and I am skipping this answer for reasons that should be obvious :) – gak Aug 05 '14 at 03:36
  • 3
    Seriously +1 to Gerald, converting a file to a string sounds like a bad idea :) – Soham Aug 27 '14 at 13:02
  • 1
    For the record, MD5 is not considered encryption but hashing -- as such, the method name should be called something along the lines of getMD5Hash(String s) – user1327961 Apr 24 '17 at 22:37
  • Since you caught the error, this code will lead to a null pointer if there is no "MD5" algorithm available. – ndw Oct 02 '19 at 21:13
  • There is no point to attempting to store the contents of a file in a String, and will likely lead to incorrect results. – President James K. Polk May 10 '20 at 19:10
  • Is there any chance android can throw NoSuchAlgorithmException for MD5 .. at least for android 23 and above? Saw couple people asking in many other threads but no concrete answer yet. – arunskrish Feb 06 '21 at 20:41
12

I had the same task and this code works excellent:

public static String fileToMD5(String filePath) {
    InputStream inputStream = null;
    try {
        inputStream = new FileInputStream(filePath);
        byte[] buffer = new byte[1024];
        MessageDigest digest = MessageDigest.getInstance("MD5");
        int numRead = 0;
        while (numRead != -1) {
            numRead = inputStream.read(buffer);
            if (numRead > 0)
                digest.update(buffer, 0, numRead);
        }
        byte [] md5Bytes = digest.digest();
        return convertHashToString(md5Bytes);
    } catch (Exception e) {
        return null;
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) { }
        }
    }
}

private static String convertHashToString(byte[] md5Bytes) {
    String returnVal = "";
    for (int i = 0; i < md5Bytes.length; i++) {
        returnVal += Integer.toString(( md5Bytes[i] & 0xff ) + 0x100, 16).substring(1);
    }
    return returnVal.toUpperCase();
}
yuralife
  • 1,545
  • 2
  • 21
  • 35
10

Here's clean little kotlin extension function. Works well on large files too.

fun File.md5(): String {
    val md = MessageDigest.getInstance("MD5")
    return this.inputStream().use { fis ->
        val buffer = ByteArray(8192)
        generateSequence {
            when (val bytesRead = fis.read(buffer)) {
                -1 -> null
                else -> bytesRead
            }
        }.forEach { bytesRead -> md.update(buffer, 0, bytesRead) }
        md.digest().joinToString("") { "%02x".format(it) }
    }
}

And the unit test to go with it:

@Test
fun `computes md5 checksum correctly`() {
    val file = File.createTempFile("test-", ".tmp")
    // did md5 on unix machine to comfirm -- put a literal LF at end to compare
    val content = "This is the content of a file." + 0x0a.toChar()
    file.writer().use { w -> w.write(content) }
    assertEquals("a149f5161e873921d84636b2a1b3aad2", file.md5())
}
broc.seib
  • 21,643
  • 8
  • 63
  • 62
8

If you're using Okio (which most apps use today, directly or indirectly by using OkHttp or Retrofit), you can also do something like this:

return File(path).source().buffer().use { source ->
   HashingSink.md5(blackholeSink()).use { sink ->
     source.readAll(sink)
     sink.hash.hex()
   }
}

This doesn't have to buffer the entire file in memory (the HashingSink will update the md5sum with every write call and then call down to blackholeSink(), which does nothing with the bytes). You can also use HashingSource instead to do something similar.

ahmedre
  • 1,684
  • 1
  • 14
  • 18
6
public static String getMd5OfFile(String filePath)
{
    String returnVal = "";
    try 
    {
        InputStream   input   = new FileInputStream(filePath); 
        byte[]        buffer  = new byte[1024];
        MessageDigest md5Hash = MessageDigest.getInstance("MD5");
        int           numRead = 0;
        while (numRead != -1)
        {
            numRead = input.read(buffer);
            if (numRead > 0)
            {
                md5Hash.update(buffer, 0, numRead);
            }
        }
        input.close();

        byte [] md5Bytes = md5Hash.digest();
        for (int i=0; i < md5Bytes.length; i++)
        {
            returnVal += Integer.toString( ( md5Bytes[i] & 0xff ) + 0x100, 16).substring( 1 );
        }
    } 
    catch(Throwable t) {t.printStackTrace();}
    return returnVal.toUpperCase();
}
XXX
  • 8,996
  • 7
  • 44
  • 53
5

If you need to calculate MD5 of the big file , you may like to use this:

Import:

import java.security.MessageDigest;

Method:

 private byte[] calculateMD5ofFile(String location) throws IOException, NoSuchAlgorithmException {
        FileInputStream fs= new FileInputStream(location);
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] buffer=new byte[bufferSize];
        int bytes=0;
        do{
            bytes=fs.read(buffer,0,bufferSize);
            if(bytes>0)
                md.update(buffer,0,bytes);

        }while(bytes>0);
        byte[] Md5Sum = md.digest();
        return Md5Sum;
    }

Refrence: https://docs.oracle.com/javase/7/docs/api/java/security/MessageDigest.html


To convert byte array to Hex. use this
public static String ByteArraytoHexString(byte[] bytes) {
    StringBuilder hexString = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(bytes[i] & 0xFF);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }
    return hexString.toString();
}

Refrence In Java, how do I convert a byte array to a string of hex digits while keeping leading zeros?

Community
  • 1
  • 1
M at
  • 1,020
  • 1
  • 12
  • 26
  • 3
    this should be the accepted answer. The reason is as @gak said in a comment of the accepted answer. – t-gao Jun 04 '19 at 08:04
  • please could you give a value for bufferSize ? – Renaud Apr 16 '20 at 10:00
  • @Renaud In case you really have to optimize it you might do something like this https://stackoverflow.com/questions/10143731/android-optimal-buffer-size otherwise use 32k – M at Apr 16 '20 at 16:44
4

I've found the following to work really well:

Process process = Runtime.getRuntime().exec("md5 "+fileLocation);
BufferedReader inputStream = new BufferedReader(new InputStreamReader(process.getInputStream()));
String result = inputStream.readLine().split(" ")[0];

This calls the built-in md5 command. The variable fileLocation is to be set to the location of the file. Of course I do recommend constructing some checks around here to check that the file exists.

Daniël van den Berg
  • 2,197
  • 1
  • 20
  • 45
3

buddy try following code

MessageDigest md = MessageDigest.getInstance("MD5");
InputStream is = new FileInputStream("file.txt");
try {
      is = new DigestInputStream(is, md);
      // read stream to EOF as normal...
    }
finally {
      is.close();
   }
byte[] digest = md.digest();
Ronak Mehta
  • 5,971
  • 5
  • 42
  • 69
2

This method worked for me, on a 131MB zip file. MD5 calculated matches that calculated on same file by AccuHash (http://www.accuhash.com)

public static String calculateMD5(File updateFile) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            Log.e("calculateMD5", "Exception while getting Digest", e);
            return null;
        }

        InputStream is;
        try {
            is = new FileInputStream(updateFile);
        } catch (FileNotFoundException e) {
            Log.e("calculateMD5", "Exception while getting FileInputStream", e);
            return null;
        }

        byte[] buffer = new byte[8192];
        int read;
        try {
            while ((read = is.read(buffer)) > 0) {
                digest.update(buffer, 0, read);
            }
            byte[] md5sum = digest.digest();
            BigInteger bigInt = new BigInteger(1, md5sum);
            String output = bigInt.toString(16);
            // Fill to 32 chars
            output = String.format("%32s", output).replace(' ', '0');
            return output;
        } catch (IOException e) {
            throw new RuntimeException("Unable to process file for MD5", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                Log.e("calculateMD5", "Exception on closing MD5 input stream", e);
            }
        }
    }   
gman413
  • 159
  • 7
2

Kotlin version:



fun File.getMD5Hash(path: String): ByteArray {
    val md = MessageDigest.getInstance("MD5")
    val stream: InputStream
    stream = FileInputStream(this)

    val buffer = ByteArray(8192)
    var read: Int
    while (stream.read(buffer).also { read = it } > 0) {
        md.update(buffer, 0, read)
    }
    stream.close()
    return md.digest()
}
Morteza Rastgoo
  • 6,772
  • 7
  • 40
  • 61
  • To prevent an exception from short-circuiting `stream.close()`, you can wrap `stream.use` around the stream reading loop and remove `stream.close()`. – Big Pumpkin Sep 08 '20 at 05:11
1
 public static String md5(String data) throws NoSuchAlgorithmException {
    // Get the algorithm:
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    // Calculate Message Digest as bytes:
    byte[] digest = md5.digest(data.getBytes(StandardCharsets.UTF_8));
    // Convert to 32-char long String:
    return String.format("%032x", new BigInteger(1, digest));
}
0

Here is my complete working code. I need to find duplicate files using checksum.

/**
 * this method is used for create check Sum further process...
 *
 * @param models    path of image.
 * @param asyncTask asyncTask of activity
 * @return return array of all files check sum.
 * <p>
 * before put BufferedInputStream
 * with BufferedInputStream (buffer 8192) with Logs
 * with BufferedInputStream (buffer 16384) with Logs
 * with BufferedInputStream (buffer 4194304) with Logs
 * with BufferedInputStream (buffer 32768) with Logs
 * with BufferedInputStream (buffer 32768) without Logs(MD5)
 * with BufferedInputStream (buffer 32768) without Logs (SHA-256)
 */
public static ArrayList<FileModel> generateCheckSum(ScanningListener scanningListener, ArrayList<FileModel> lstAllFile, AsyncTask asyncTask) {
    FileInputStream fis;
    MessageDigest md;
    byte[] buffer;
    int numOfBytesRead;
    byte[] hash;

    long startTime = System.currentTimeMillis();
    for (FileModel s : lstAllFile) {

        if (scanningListener != null)
            scanningListener.onGoingProgress(lstAllFile.size(),lstAllFile.indexOf(s));
        try {
            if (asyncTask.isCancelled()) {
                break;
            }

            fis = new FileInputStream(s.getFilePath());
            md = MessageDigest.getInstance("MD5");
            buffer = new byte[16384];//(1024*2048)


            while ((numOfBytesRead = fis.read(buffer)) > 0) {
                md.update(buffer, 0, numOfBytesRead);
            }

            hash = md.digest();
            s.setChecksum(convertHashToString(hash));
            CustomLog.error("path", String.valueOf(s.getFilePath()));
        } catch (IOException ex) {
            CustomLog.error("IOException", String.valueOf(ex));
        } catch (NoSuchAlgorithmException ex) {
            CustomLog.error("NoSuchAlgorithmException ", String.valueOf(ex));
        }
    }
    long endTime = System.currentTimeMillis();

    long totalTime = endTime - startTime;
    CustomLog.error("Total Time : ", TimeUtils.getDateIn24HrsFormatInUTC(totalTime));
    return lstAllFile;
}

convertHashToString(hash)

/**
 * this method is help for convert hash value into string file and return hash code.
 *
 * @param hash byte array.
 * @return return string of hash code
 */
private static String convertHashToString(byte[] hash) {
    StringBuilder returnVal = new StringBuilder();
    for (byte md5Byte : hash) {
        returnVal.append(Integer.toString((md5Byte & 0xff) + 0x100, 16).substring(1));
    }
    return returnVal.toString();
}

This method will give you a hashmap of all the given files.

I've tried many different types of buffer size as well as MD5 and SHA-1 that you can see in the comments section

Mehul Boghra
  • 202
  • 3
  • 11
0

I use these two extensions in Kotlin:

fun File.calcHash(algorithm: String = "MD5", bufferSize: Int = 1024): ByteArray {
    this.inputStream().use { input ->
        val buffer = ByteArray(bufferSize)
        val digest = MessageDigest.getInstance(algorithm)

        read@ while (true) {
            when (val bytesRead = input.read(buffer)) {
                -1 -> break@read
                else -> digest.update(buffer, 0, bytesRead)
            }
        }

        return digest.digest()
    }
}

fun ByteArray.toHexString(): String {
    return this.fold(StringBuilder()) { result, b -> result.append(String.format("%02X", b)) }.toString()
}
t3chb0t
  • 16,340
  • 13
  • 78
  • 118
0
    fun md5(file: File): String {
        val digest = MessageDigest.getInstance(MD5_ALGORITHM)
        file.inputStream().buffered(BUFFER_SIZE).use { it.iterator().forEach(digest::update) }
        return digest.digest().joinToString("") { "%02x".format(it) }
    }
Juan Rada
  • 3,513
  • 1
  • 26
  • 26
0

With OKio, it is a one-liner:

val md5_as_hex_string = Okio.buffer(Okio.source(file).readByteString().md5().hex()
Interkot
  • 697
  • 8
  • 10