Abrufen der MD5-Prüfsumme einer Datei in Java

510

Ich möchte Java verwenden, um die MD5-Prüfsumme einer Datei abzurufen. Ich war wirklich überrascht, aber ich konnte nichts finden, das zeigt, wie man die MD5-Prüfsumme einer Datei erhält.

Wie wird es gemacht?

Jack
quelle
Vielleicht dies helfen. Sie könnten auch die Spezifikation nachschlagen, aber das würde mehr Arbeit erfordern, da es kompliziert ist.
Waynecolvin
4
Denken Sie daran, dass laut der jüngsten Forschung "MD5 als kryptografisch defekt und für die weitere Verwendung ungeeignet angesehen werden sollte". en.wikipedia.org/wiki/MD5
Zakharia Stanley
80
MD5 wird nicht mehr als kryptografisch sicher angesehen, reicht jedoch zur Überprüfung der Dateikonsistenz aus und ist schneller als SHA.
Jiggy
2
@ZakhariaStanley Dies ist eine Frage zur Prüfsumme.
iPherian
Die kanonische Verwendung für MD5-Prüfsummen für Dateien besteht darin, das feindliche Ersetzen verteilter Dateien zu vermeiden. Dort ist es unsicher. Aber in einem Szenario, in dem feindliche Exploits keine Rolle spielen, ist es perfekt geeignet.
Keith Tyler

Antworten:

541

Es gibt einen Dekorator für Eingabestreams, java.security.DigestInputStreammit dem Sie den Digest berechnen können, während Sie den Eingabestream wie gewohnt verwenden, anstatt einen zusätzlichen Durchlauf über die Daten durchführen zu müssen.

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();
erickson
quelle
4
Ich bin damit einverstanden, eine sehr elegante Methode, um die Prüfsumme im laufenden Betrieb zu berechnen, wenn Sie bereits etwas mit den Bytes tun (dh sie über eine HTTP-Verbindung einlesen).
Marc Novakowski
2
@AlPhaba Hast du das isals InputStreamoder oder deklariert FileInputStream? Klingt wie Sie verwendet FileInputStream, was diesen Fehler verursachen würde.
Erickson
1
@barwnikk Es funktioniert gut in Java 8. MethodNotFoundist keine Ausnahme von Standard-Java; Vielleicht sprechen Sie über einen Compilerfehler? In jedem Fall liegt ein lokales Konfigurationsproblem oder ein Problem mit anderem Code vor, wenn es bei Ihnen nicht funktioniert.
Erickson
4
@barwnikk Auch dies ist Ihr lokales Konfigurationsproblem. Dies ist gültiger Java 7- und Java 8-Code. Wenn Sie mit Werkzeugen aus dem Jahr 2006 nicht weiterkommen, müssen Sie sich anpassen.
Erickson
5
@erickson Sie aktualisieren das MessageDigest-Objekt nicht mit dem Dateiinhalt. Rt? Dieser Code gibt immer den gleichen Digest aus.
Sunil
302

Verwenden Sie DigestUtils aus der Apache Commons Codec- Bibliothek:

try (InputStream is = Files.newInputStream(Paths.get("file.zip"))) {
    String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(is);
}
Leif Gruenwoldt
quelle
1
Funktioniert nicht für mich in meinem Android-Code Ich erhalte diesen Fehler ... java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString bei org.apache.commons.codec.digest.DigestUtils.md5Hex (DigestUtils.java:215)
JPM
@JPM Angenommen, Sie haben das heruntergeladen und commons-codec.jarbereits auf Ihren Klassenpfad gesetzt?
Leif Gruenwoldt
Ja, und ich habe in mein Android-Projekt exportiert. Ich kann den Code durchgehen und die Klasse befindet sich in den Quelldateien. Seltsam, muss ein Android-Eclipse-Problem sein.
JPM
1
Ich hatte das gleiche Problem, aber es wurde durch diesen Code behoben: FileInputStream fis = new FileInputStream (neue Datei (filePath)); Byte-Daten [] = org.apache.commons.codec.digest.DigestUtils.md5 (fis); char md5Chars [] = Hex.encodeHex (Daten); String md5 = String.valueOf (md5Chars); `
Dmitry_L
1
Nett! Bei neuen Projekten denke ich immer zweimal darüber nach, bevor ich eine neue Abhängigkeit hinzufüge, aber bei bestehenden Projekten muss ich nur prüfen, ob die Bibliothek bereits vorhanden ist, um sie zu verwenden. +1
OscarRyz
164

In Real's Java-How-to gibt es ein Beispiel für die Verwendung der MessageDigest- Klasse.

Auf dieser Seite finden Sie auch Beispiele für CRC32 und 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();
       }
   }
}
Bill die Eidechse
quelle
70
Ja ... nach 11 Jahren immer noch online! :-)
RealHowTo
Das Beispiel in Real's Java-How-To funktioniert perfekt und war einfach zu implementieren.
Bakoyaro
Die Leseschleife ist etwas ungeschickt. read()gibt nicht Null zurück und a do/whileist nicht wirklich angemessen.
Marquis von Lorne
10
@EJP Vielen Dank für Ihr rechtzeitiges Feedback.
Bill the Lizard
Byte [] Puffer = neues Byte [1024]; Können wir die Größe von 1024 auf etwas Optimaleres ändern?
Jalpesh
90

Die API com.google.common.hash bietet:

  • Eine einheitliche benutzerfreundliche API für alle Hash-Funktionen
  • Seedable 32- und 128-Bit-Implementierungen von murmur3
  • Die Adapter md5 (), sha1 (), sha256 (), sha512 () ändern nur eine Codezeile, um zwischen diesen zu wechseln, und murmeln.
  • goodFastHash (int bits), wenn es Ihnen egal ist, welchen Algorithmus Sie verwenden
  • Allgemeine Dienstprogramme für HashCode-Instanzen wie combinOrdered / kombinierenUnordered

Lesen Sie das Benutzerhandbuch ( IO Explained , Hashing Explained ).

Files.hash()Berechnet für Ihren Anwendungsfall den Digest-Wert für eine Datei und gibt ihn zurück.

Zum Beispiel a Digest-Berechnung (SHA-1 in MD5 ändern, um MD5-Digest zu erhalten)

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

Beachten Sie, dass ist viel schneller als , also benutze wenn Sie keine kryptografisch sichere Prüfsumme benötigen. Beachten Sie auch das sollte nicht zum Speichern von Passwörtern und dergleichen verwendet werden, da es für die Verwendung von Passwörtern zu einfach ist, Gewalt anzuwenden , oder stattdessen.

Für den Langzeitschutz mit Hashes erhöht ein Merkle-Signaturschema die Sicherheit, und die von der Europäischen Kommission geförderte Post Quantum Cryptography Study Group hat die Verwendung dieser Kryptographie zum Langzeitschutz gegen Quantencomputer empfohlen ( Ref ).

Beachten Sie, dass hat eine höhere Kollisionsrate als die anderen.

oluies
quelle
Welcher Teil von Files.hash, wie oben angegeben, deckt Files.hash nicht ab?
Oluies
2
Das Files.hash()ist als veraltet markiert, der empfohlene Weg ist:Files.asByteSource(file).hash(Hashing.sha1())
erkfel
1
Und ab Januar 2018 Hashing.sha1()ist als veraltet markiert. Die Funktion Hashing.sha256()wird stattdessen empfohlen. Quelle
MagicLegend
60

Verwenden von nio2 (Java 7+) und ohne externe Bibliotheken:

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

So vergleichen Sie das Ergebnis mit einer erwarteten Prüfsumme:

String expected = "2252290BC44BEAD16AA1BF89948472E8";
String actual = DatatypeConverter.printHexBinary(hash);
System.out.println(expected.equalsIgnoreCase(actual) ? "MATCH" : "NO MATCH");
Assylien
quelle
@Arash ja absolut - danke. Ich habe die JDK Files-Klasse und die von Guava verwechselt.
Assylias
Ich mag diese Lösung mehr als die von erickson, da sie mit Optionals umwickelt werden kann, um reine funktionale Programmierung zu verwenden
Gabriel Hernandez
2
Bei einer großen Datei wird viel Speicher benötigt, da die gesamte Datei gelesen und dann dem Digest zugeführt wird, anstatt Chunks zu lesen und sie beim Lesen zu "verdauen".
Bernie
39

Guava bietet jetzt eine neue, konsistente Hashing-API, die viel benutzerfreundlicher ist als die verschiedenen im JDK bereitgestellten Hashing-APIs. Siehe Hashing erklärt . Für eine Datei können Sie die MD5-Summe, CRC32 (mit Version 14.0+) oder viele andere Hashes einfach abrufen:

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();
ColinD
quelle
32

OK. Ich musste hinzufügen. Einzeilige Implementierung für diejenigen, die bereits von Spring und Apache Commons abhängig sind oder diese hinzufügen möchten:

DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file))

Nur für und Apache Commons Option (credit @duleshi):

DigestUtils.md5Hex(FileUtils.readFileToByteArray(file))

Hoffe das hilft jemandem.

MickJ
quelle
1
Es istDigestUtils.md5Hex(FileUtils.readFileToByteArray(file))
Duleshi
Die auf Commons basierende Lösung von David Onter ist besser, da nicht eine ganze Datei in den Speicher eingelesen wird.
Fran Marzoa
Zumindest müssen Spring 5 Sie DigestUtils.md5Digest(InputStream inputStream)den MD5-Digest und die DigestUtils.md5DigestAsHex(InputStream inputStream)hexadezimale Zeichenfolgendarstellung der MD5-Digest-Methoden berechnen , ohne eine ganze Datei in den Speicher einzulesen.
Mike Shauneu
24

Ein einfacher Ansatz ohne Bibliotheken von Drittanbietern, die Java 7 verwenden

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

Wenn Sie dieses Byte-Array drucken müssen. Verwenden Sie wie unten

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

Wenn Sie eine Hex-Zeichenfolge aus diesem Digest benötigen. Verwenden Sie wie unten

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

Dabei ist DatatypeConverter javax.xml.bind.DatatypeConverter

sunil
quelle
Warum das toUpperCase?
EdgeCaseBerg
@edgecaseberg nur für die Hex-Zeichenfolge sehen gut aus, während Sie es auf Konsole
drucken
Ich musste toLowerCase () anstelle von toUpperCase () verwenden.
Pracht
14

Ich musste dies kürzlich nur für eine dynamische Zeichenfolge tun, MessageDigestdie den Hash auf vielfältige Weise darstellen kann. Um die Signatur der Datei zu erhalten, wie Sie sie mit dem Befehl md5sum erhalten würden, musste ich Folgendes tun:

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

Dies beantwortet offensichtlich nicht Ihre Frage, wie es speziell für eine Datei gemacht werden soll. Die obige Antwort behandelt diese Ruhe gut. Ich habe gerade viel Zeit damit verbracht, die Summe so zu gestalten, dass sie wie die meisten Anwendungen aussieht, und dachte, Sie könnten auf die gleichen Probleme stoßen.

Brian Gianforcaro
quelle
Die Signatur ist der Digest im Hexadezimalformat. Ich fand auch, dass die hexadezimale Darstellung dort funktioniert, wo, wie Sie sagen, andere Darstellungen nicht funktionieren. Vielen Dank für das Aufstellen.
Amit
Das ist gut, .toString(16)wirft aber führende Nullen weg. String.format("%032x", ...)vielleicht besser.
Harold vor
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());
}

Oder Sie erhalten weitere Informationen http://www.asjava.com/core-java/java-md5-example/

Marmelade
quelle
9
String checksum = DigestUtils.md5Hex(new FileInputStream(filePath));
Ravikiran Kalal
quelle
9

Wir haben Code verwendet, der dem obigen Code in einem früheren Beitrag ähnelt

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

Achten Sie jedoch auf die Verwendung BigInteger.toString()hier, da dadurch führende Nullen abgeschnitten werden ... (Beispiel: Versuchen Sie s = "27", die Prüfsumme sollte sein. "02e74f10e0327ad868d138f2b4fdd6f0")

Ich stimme dem Vorschlag zu, Apache Commons Codec zu verwenden, und habe unseren eigenen Code dadurch ersetzt.

user552999
quelle
1
Wow, ich habe mich mit einem Problem befasst, bei dem das MD5-Zeug für alles perfekt funktionierte, außer dass eine Datei nur eine 31-hexadezimale Ausgabe lieferte und die md5-Prüfsummen nicht bestanden haben. Das Abschneiden führender Nullen ist ein großer Schmerz ... Vielen Dank für Ihre Notiz.
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
   }
}
FX
quelle
8

Sehr schnelle und saubere Java-Methode, die nicht auf externen Bibliotheken basiert:

(Ersetzen Sie MD5 einfach durch SHA-1, SHA-256, SHA-384 oder SHA-512, wenn Sie diese möchten.)

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);
}
David
quelle
6

Eine weitere Implementierung: Schnelle MD5-Implementierung in Java

String hash = MD5.asHex(MD5.getHash(new File(filename)));
Lukasz R.
quelle
Ich kann die Methode MD5.asHex()in JDK 1.8.0 242 nicht finden.
cbaldan
6

Standard Java Runtime Environment Weg :

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

Das Ergebnis entspricht dem Dienstprogramm linux md5sum.

gotozero
quelle
6

Hier ist eine einfache Funktion, die den Code von Sunil umschließt, sodass eine Datei als Parameter verwendet wird. Die Funktion benötigt keine externen Bibliotheken, benötigt jedoch 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);            
    }


}

Beispielausgabe:

hex=B117DD0C3CBBD009AC4EF65B6D75C97B
stackoverflowuser2010
quelle
3

Wenn Sie ANT zum Erstellen verwenden, ist dies kinderleicht. Fügen Sie Ihrer build.xml Folgendes hinzu:

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

Dabei ist jarFile die JAR, für die Sie MD5 generieren möchten, und toDir ist das Verzeichnis, in dem Sie die MD5-Datei ablegen möchten.

Mehr Infos hier.

Matt Brock
quelle
3

Google Guava bietet eine neue API. Finden Sie die folgende:

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
Balaji Boggaram Ramanarayan
quelle
3

Hier ist eine praktische Variante, die InputStream.transferTo()Java 9 und OutputStream.nullOutputStream()Java 11 verwendet. Sie erfordert keine externen Bibliotheken und muss nicht die gesamte Datei in den Speicher laden.

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

und

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

kehrt zurück

"e30fa2784ba15be37833d569280e2163c6f106506dfb9b07dde67a24bfb90da65c661110cf2c5c6f71185754ee5ae3fd83a5465c92f72abd888b03187229da29"
Rechnung
quelle
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();
}
XXX
quelle