Ottenere il checksum MD5 di un file in Java


510

Sto cercando di utilizzare Java per ottenere il checksum MD5 di un file. Sono stato davvero sorpreso, ma non sono stato in grado di trovare nulla che mostri come ottenere il checksum MD5 di un file.

Com'è fatto?


Forse questo aiuterà. Potresti anche cercare le specifiche, ma ci vorrebbe di più perché è complicato.
waynecolvin,

4
Tieni presente che secondo la recente ricerca "MD5 dovrebbe essere considerato crittograficamente rotto e inadatto per un ulteriore utilizzo". en.wikipedia.org/wiki/MD5
Zakharia Stanley

80
MD5 non è più considerato crittograficamente sicuro, ma è ancora sufficiente per convalidare la coerenza dei file ed è più veloce di SHA.
jiggy,

2
@ZakhariaStanley Questa è una domanda sul checksum.
iPherian,

L'uso canonico dei checksum MD5 sui file è di evitare sostituzioni ostili di file distribuiti. Ecco dove non è sicuro. Ma in uno scenario in cui gli exploit ostili non sono un problema, è perfettamente adatto.
Keith Tyler,

Risposte:


541

C'è un decoratore del flusso di input java.security.DigestInputStream, in modo che tu possa calcolare il digest mentre usi il flusso di input come faresti normalmente, invece di dover fare un passaggio extra sui dati.

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

4
Sono d'accordo, un modo molto elegante per calcolare il checksum al volo se stai già facendo qualcosa con i byte (cioè leggendoli su da una connessione HTTP).
Marc Novakowski il

2
@AlPhaba Hai dichiarato il iscome un InputStreamo un FileInputStream? Sembra che tu l'abbia usato FileInputStream, il che causerebbe questo errore.
Erickson,

1
@barwnikk Funziona bene in Java 8. MethodNotFoundnon fa eccezione a Java standard; forse stai parlando di un errore del compilatore? In ogni caso, se non funziona per te, è un problema di configurazione locale o un altro codice.
Erickson,

4
@barwnikk Ancora una volta, questo è il problema di configurazione locale. Questo è un codice Java 7 e Java 8 valido. Se sei bloccato con gli strumenti del 2006, dovrai adattarti.
Erickson,

5
@erickson Non si sta aggiornando l'oggetto MessageDigest con il contenuto del file. RT? Questo codice stamperà sempre lo stesso digest.
domenica

302

Usa DigestUtils dalla libreria Codec di Apache Commons :

try (InputStream is = Files.newInputStream(Paths.get("file.zip"))) {
    String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(is);
}

1
Non funziona per me nel mio codice Android Ricevo questo errore ... java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString su org.apache.commons.codec.digest.DigestUtils.md5Hex (DigestUtils.java:215)
JPM

@JPM Presumi di aver scaricato e inserito commons-codec.jargià il tuo percorso di classe?
Leif Gruenwoldt,

sì lì e ho esportato nel mio progetto Android .. Posso scorrere il codice e la classe è lì nei file sorgente ... strano, deve essere un problema di Android Eclipse.
JPM

1
Ho avuto lo stesso problema, ma risolto con questo codice `FileInputStream fis = new FileInputStream (new File (filePath)); byte data [] = org.apache.commons.codec.digest.DigestUtils.md5 (fis); char md5Chars [] = Hex.encodeHex (data); String md5 = String.valueOf (md5Chars); `
Dmitry_L

1
Bello! Per i nuovi progetti, penso sempre due volte prima di aggiungere una nuova dipendenza, ma per il progetto esistente devo solo verificare se la libreria è già lì per usarla. +1
OscarRyz

164

C'è un esempio nel Java-How-to di Real che utilizza la classe MessageDigest .

Controlla quella pagina per esempi usando anche CRC32 e SHA-1.

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

70
Sì ... ancora online dopo 11 anni! :-)
RealHowTo

L'esempio di Java-How-To di Real funziona perfettamente ed era semplice da implementare.
Bakoyaro,

Il ciclo di lettura è un po 'goffo. read()non restituirà zero e a do/whilenon è davvero appropriato.
Marchese di Lorne,

10
@EJP Grazie per il tuo feedback tempestivo.
Bill the Lizard,

byte [] buffer = new byte [1024]; possiamo cambiare la dimensione da 1024 a qualcosa di più ottimale?
Jalpesh,

90

L' API com.google.common.hash offre:

  • Un'API unificata di facile utilizzo per tutte le funzioni hash
  • Implementazioni di murmur3 a 32 e 128 bit con seeding
  • Adattatori md5 (), sha1 (), sha256 (), sha512 (), cambia solo una riga di codice per passare da una all'altra e mormora.
  • goodFastHash (int bit), per quando non ti interessa quale algoritmo usi
  • Utilità generali per le istanze di HashCode, come combinare Ordinato / CombinareUnordinato

Leggi la Guida per l'utente ( IO spiegato , hash spiegato ).

Per il tuo caso d'uso Files.hash()calcola e restituisce il valore digest per un file.

Ad esempio a calcolo digest (modifica SHA-1 in MD5 per ottenere digest MD5)

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

Nota che è molto più veloce di , quindi usa se non hai bisogno di un checksum crittograficamente sicuro. Si noti anche che non dovrebbe essere usato per archiviare password e simili poiché è facile da forzare, per l'uso delle password , o anziché.

Per la protezione a lungo termine con hash uno schema di firma Merkle aggiunge sicurezza e il gruppo di studio sulla crittografia post quantistica sponsorizzato dalla Commissione europea ha raccomandato l'uso di questa crittografia per la protezione a lungo termine contro i computer quantistici ( rif ).

Nota che ha un tasso di collisione più elevato rispetto agli altri.


Quale parte di Files.hash come indicato sopra non copre Files.hash?
Oluies,

2
Il Files.hash()è contrassegnato come deprecato, il metodo consigliato è:Files.asByteSource(file).hash(Hashing.sha1())
erkfel

1
E a partire da gennaio 2018 Hashing.sha1()è contrassegnato come obsoleto. Si Hashing.sha256()consiglia invece la funzione. fonte
MagicLegend,

60

Utilizzo di nio2 (Java 7+) e nessuna libreria esterna:

byte[] b = Files.readAllBytes(Paths.get("/path/to/file"));
byte[] hash = MessageDigest.getInstance("MD5").digest(b);

Per confrontare il risultato con un checksum previsto:

String expected = "2252290BC44BEAD16AA1BF89948472E8";
String actual = DatatypeConverter.printHexBinary(hash);
System.out.println(expected.equalsIgnoreCase(actual) ? "MATCH" : "NO MATCH");

@Arash sì assolutamente - grazie. Ho confuso la classe JDK Files e quella di Guava.
assylias

Mi piace questa soluzione più di quella di Erickson poiché può essere inclusa in Optionals per utilizzare la pura programmazione in stile funzionale
Gabriel Hernandez,

2
Per un file di grandi dimensioni questo utilizzerà molta memoria poiché l'intero file viene letto e quindi inviato al digest anziché leggere blocchi e "digerirli" mentre vengono letti.
Bernie,

39

Guava ora fornisce una nuova e coerente API di hash che è molto più user-friendly rispetto alle varie API di hashing fornite nel JDK. Vedi Hashing spiegato . Per un file, puoi ottenere facilmente la somma MD5, CRC32 (con la versione 14.0+) o molti altri hash:

HashCode md5 = Files.hash(file, Hashing.md5());
byte[] md5Bytes = md5.asBytes();
String md5Hex = md5.toString();

HashCode crc32 = Files.hash(file, Hashing.crc32());
int crc32Int = crc32.asInt();

// the Checksum API returns a long, but it's padded with 0s for 32-bit CRC
// this is the value you would get if using that API directly
long checksumResult = crc32.padToLong();

32

Ok. Ho dovuto aggiungere. Una linea di implementazione per coloro che hanno già la dipendenza da Spring e Apache Commons o hanno in programma di aggiungerlo:

DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file))

Solo opzione for commons Apache (credit @duleshi):

DigestUtils.md5Hex(FileUtils.readFileToByteArray(file))

Spero che questo aiuti qualcuno.


1
ÈDigestUtils.md5Hex(FileUtils.readFileToByteArray(file))
duleshi il

La soluzione basata su commons di David Onter è migliore perché non legge un intero file in memoria.
Fran Marzoa,

Almeno per Spring 5 te devi DigestUtils.md5Digest(InputStream inputStream)calcolare il digest MD5 e DigestUtils.md5DigestAsHex(InputStream inputStream)per la rappresentazione esadecimale della stringa dei metodi digest MD5 senza leggere un intero file in memoria.
Mike Shauneu,

24

Un approccio semplice senza librerie di terze parti che utilizzano Java 7

String path = "your complete file path";
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(Files.readAllBytes(Paths.get(path)));
byte[] digest = md.digest();

Se è necessario stampare questo array di byte. Usa come di seguito

System.out.println(Arrays.toString(digest));

Se hai bisogno di stringhe esadecimali da questo digest. Usa come di seguito

String digestInHex = DatatypeConverter.printHexBinary(digest).toUpperCase();
System.out.println(digestInHex);

dove DatatypeConverter è javax.xml.bind.DatatypeConverter


Perché il toUpperCase?
EdgeCaseBerg il

@edgecaseberg solo per la stringa esadecimale sembra buono mentre lo stampa sulla console
sunil

Ho scoperto che dovevo usare toLowerCase () invece di toUpperCase ().
Splendore

14

Di recente ho dovuto farlo solo per una stringa dinamica, in MessageDigestgrado di rappresentare l'hash in numerosi modi. Per ottenere la firma del file come si otterrebbe con il comando md5sum ho dovuto fare qualcosa del genere:

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

Questo ovviamente non risponde alla tua domanda su come farlo specificamente per un file, la risposta di cui sopra si occupa di quella calma silenziosa. Ho appena trascorso molto tempo a fare in modo che la somma assomigliasse alla maggior parte delle applicazioni, e ho pensato che potresti avere lo stesso problema.


La firma è il digest in formato esadecimale. Anch'io ho trovato la rappresentazione esadecimale per funzionare dove, come dici tu, altre rappresentazioni non funzionano. Grazie per averlo presentato.
Amit

Questo è buono, ma .toString(16)eliminerà gli zeri iniziali. String.format("%032x", ...)forse meglio.
Harold

11
public static void main(String[] args) throws Exception {
    MessageDigest md = MessageDigest.getInstance("MD5");
    FileInputStream fis = new FileInputStream("c:\\apache\\cxf.jar");

    byte[] dataBytes = new byte[1024];

    int nread = 0;
    while ((nread = fis.read(dataBytes)) != -1) {
        md.update(dataBytes, 0, nread);
    };
    byte[] mdbytes = md.digest();
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < mdbytes.length; i++) {
        sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
    }
    System.out.println("Digest(in hex format):: " + sb.toString());
}

Oppure puoi ottenere maggiori informazioni http://www.asjava.com/core-java/java-md5-example/



9

Stavamo usando un codice che ricorda il codice sopra in un post precedente usando

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

Tuttavia, fai attenzione all'utilizzo BigInteger.toString()qui, poiché troncerà gli zeri iniziali ... (ad esempio, prova s = "27", il checksum dovrebbe essere"02e74f10e0327ad868d138f2b4fdd6f0" )

Secondo il suggerimento di usare Apache Commons Codec, ho sostituito il nostro codice con quello.


1
Wow, stavo esaminando un problema in cui la roba MD5 funzionava perfettamente per tutto, tranne che un file ci dava solo un output di 31 cifre esadecimali e non riusciva a ottenere i md5checksums. che troncare i primi 0 è un dolore enorme ... Grazie per la tua nota.
Mike

8
public static String MD5Hash(String toHash) throws RuntimeException {
   try{
       return String.format("%032x", // produces lower case 32 char wide hexa left-padded with 0
      new BigInteger(1, // handles large POSITIVE numbers 
           MessageDigest.getInstance("MD5").digest(toHash.getBytes())));
   }
   catch (NoSuchAlgorithmException e) {
      // do whatever seems relevant
   }
}

8

Metodo Java molto veloce e pulito che non si basa su librerie esterne:

(Sostituisci semplicemente MD5 con SHA-1, SHA-256, SHA-384 o SHA-512 se vuoi quelli)

public String calcMD5() throws Exception{
        byte[] buffer = new byte[8192];
        MessageDigest md = MessageDigest.getInstance("MD5");

        DigestInputStream dis = new DigestInputStream(new FileInputStream(new File("Path to file")), md);
        try {
            while (dis.read(buffer) != -1);
        }finally{
            dis.close();
        }

        byte[] bytes = md.digest();

        // bytesToHex-method
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }

        return new String(hexChars);
}


6

Modo Java Runtime Environment standard :

public String checksum(File file) {
  try {
    InputStream fin = new FileInputStream(file);
    java.security.MessageDigest md5er =
        MessageDigest.getInstance("MD5");
    byte[] buffer = new byte[1024];
    int read;
    do {
      read = fin.read(buffer);
      if (read > 0)
        md5er.update(buffer, 0, read);
    } while (read != -1);
    fin.close();
    byte[] digest = md5er.digest();
    if (digest == null)
      return null;
    String strDigest = "0x";
    for (int i = 0; i < digest.length; i++) {
      strDigest += Integer.toString((digest[i] & 0xff) 
                + 0x100, 16).substring(1).toUpperCase();
    }
    return strDigest;
  } catch (Exception e) {
    return null;
  }
}

Il risultato è uguale all'utilità md5sum di linux.


6

Ecco una semplice funzione che avvolge il codice di Sunil in modo che prenda un file come parametro. La funzione non richiede librerie esterne, ma richiede Java 7.

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.bind.DatatypeConverter;

public class Checksum {

    /**
     * Generates an MD5 checksum as a String.
     * @param file The file that is being checksummed.
     * @return Hex string of the checksum value.
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    public static String generate(File file) throws NoSuchAlgorithmException,IOException {

        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        messageDigest.update(Files.readAllBytes(file.toPath()));
        byte[] hash = messageDigest.digest();

        return DatatypeConverter.printHexBinary(hash).toUpperCase();
    }

    public static void main(String argv[]) throws NoSuchAlgorithmException, IOException {
        File file = new File("/Users/foo.bar/Documents/file.jar");          
        String hex = Checksum.generate(file);
        System.out.printf("hex=%s\n", hex);            
    }


}

Esempio di output:

hex=B117DD0C3CBBD009AC4EF65B6D75C97B

3

Se stai usando ANT per costruire, questo è semplicissimo. Aggiungi quanto segue a build.xml:

<checksum file="${jarFile}" todir="${toDir}"/>

Dove jarFile è il JAR su cui si desidera generare MD5 e toDir è la directory in cui si desidera posizionare il file MD5.

Maggiori informazioni qui.


3

Google guava fornisce una nuova API. Trova quello qui sotto:

public static HashCode hash(File file,
            HashFunction hashFunction)
                     throws IOException

Computes the hash code of the file using hashFunction.

Parameters:
    file - the file to read
    hashFunction - the hash function to use to hash the data
Returns:
    the HashCode of all of the bytes in the file
Throws:
    IOException - if an I/O error occurs
Since:
    12.0

3

Ecco una comoda variante che utilizza InputStream.transferTo()Java 9 e OutputStream.nullOutputStream()Java 11. Non richiede librerie esterne e non è necessario caricare l'intero file in memoria.

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

e

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

ritorna

"e30fa2784ba15be37833d569280e2163c6f106506dfb9b07dde67a24bfb90da65c661110cf2c5c6f71185754ee5ae3fd83a5465c92f72abd888b03187229da29"

2
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();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.