75

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.

16 Answers 16

159

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); } } } } 
Sign up to request clarification or add additional context in comments.

4 Comments

You're welcome. And yes, GPL. It propagates to the whole app.
Useless because of GPL
Is there some explanation as to what is going on in this piece of code ? Would be really helpful.
@Mensly, all subscriber content is CC-BY-SA. See the 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.
45

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.

8 Comments

Beware that before Android 4.2.1 MessageDigest.getInstance() was not thread safe. Check bug report and fix. So if you're using HttpResponseCache you better check for something different
this method is removing initial 0 from final md5 sting:(. how to overcome this ?
I came here to MD5 a 700MB file, and I am skipping this answer for reasons that should be obvious :)
Seriously +1 to Gerald, converting a file to a string sounds like a bad idea :)
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)
|
13

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()) } 

2 Comments

why a buffer size of 8192 bytes?
I suppose your mileage may vary depending on where this code is used. I think on an Android device the filesystem block size is 4K (??) in which case 4096 might be a better buffer size for file IO (since it would minimize the number read calls in the kernel for that sequence of data). But I've never measured anything to know how those buffer sizes would compare in performance.
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(); } 

Comments

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.

2 Comments

This way even simpler: val md5_as_hex_string = Okio.buffer(Okio.source(file).readByteString().md5().hex()
@Interkot you missed a closing bracket, but thank you
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(); } 

Comments

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?

3 Comments

this should be the accepted answer. The reason is as @gak said in a comment of the accepted answer.
please could you give a value for bufferSize ?
@Renaud In case you really have to optimize it you might do something like this stackoverflow.com/questions/10143731/… otherwise use 32k
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.

5 Comments

Are you sure this binary exists on every Android install? Here at my device, it is called md5sum.
@bk138 not any more. I do think it's worth checking if it does before performing any of the other mentioned methods, as the speed of this binary is way better than of any Java-implementation.
works great but not for sha256 I think. At least I could not find any shasum, sha256sum, openssl dgst -sha256, ... on my AVD.
Interesting alternative to the 10 or so identical answers.
java.io.IOException: Cannot run program "md5": error=13, Permission denied. UPD md5sum is ok!
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(); 

Comments

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); } } } 

Comments

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() } 

1 Comment

To prevent an exception from short-circuiting stream.close(), you can wrap stream.use around the stream reading loop and remove stream.close().
2
 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)); } 

Comments

1
 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) } } 

Comments

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

Comments

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() } 

Comments

0

With OKio, it is a one-liner:

val md5_as_hex_string = Okio.buffer(Okio.source(file).readByteString().md5().hex() 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.