Obtener la suma de comprobación MD5 de un archivo en Java


510

Estoy buscando utilizar Java para obtener la suma de comprobación MD5 de un archivo. Me sorprendió mucho, pero no he podido encontrar nada que muestre cómo obtener la suma de comprobación MD5 de un archivo.

Como se hace


Quizás esto ayude. También puede buscar la especificación, pero eso requeriría más trabajo ya que es complicado.
waynecolvin

44
Tenga en cuenta que según la investigación reciente "MD5 debe considerarse criptográficamente roto y no apto para su uso posterior". en.wikipedia.org/wiki/MD5
Zakharia Stanley

80
MD5 ya no se considera criptográficamente seguro, pero sigue siendo suficiente para validar la consistencia del archivo y es más rápido que SHA.
jiggy

2
@ZakhariaStanley Esta es una pregunta sobre la suma de comprobación.
iPherian

El uso canónico para las sumas de verificación MD5 en los archivos es evitar reemplazos hostiles de archivos distribuidos. Ahí es donde no es seguro. Pero en un escenario donde las hazañas hostiles no son una preocupación, es perfectamente adecuado.
Keith Tyler

Respuestas:


541

Hay un decorador de flujo de entrada java.security.DigestInputStream, para que pueda calcular el resumen mientras usa el flujo de entrada como lo haría normalmente, en lugar de tener que hacer un pase adicional sobre los datos.

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

44
Estoy de acuerdo, es una forma muy elegante de calcular la suma de verificación sobre la marcha si ya está haciendo algo con los bytes (es decir, leerlos desde una conexión HTTP).
Marc Novakowski,

2
@AlPhaba ¿Declaraste el iscomo un InputStreamo un FileInputStream? Suena como lo usaste FileInputStream, lo que causaría este error.
erickson

1
@barwnikk Funciona bien en Java 8. MethodNotFoundno es una excepción de Java estándar; tal vez estás hablando de un error del compilador? En cualquier caso, si no funciona para usted, es un problema de configuración local o un problema con otro código.
erickson

44
@barwnikk Nuevamente, ese es su problema de configuración local. Este es un código válido de Java 7 y Java 8. Si está atrapado con herramientas de 2006, tendrá que adaptarse.
erickson

55
@erickson No está actualizando el objeto MessageDigest con el contenido del archivo. Rt? Este código imprimirá siempre un mismo resumen.
sunil

302

Utilice DigestUtils de la biblioteca de códecs de Apache Commons :

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

1
No funciona para mí en mi código de Android Obtengo este error ... java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString en org.apache.commons.codec.digest.DigestUtils.md5Hex (DigestUtils.java:215)
JPM

@JPM ¿Presumiste que descargaste y pusiste el commons-codec.jaren tu classpath ya?
Leif Gruenwoldt

Sí, y exporté en mi proyecto de Android ... Puedo recorrer el código y la clase está allí en los archivos de origen ... raro, debe ser un problema de Android Eclipse.
JPM

1
Tuve el mismo problema, pero lo solucionó con este código `FileInputStream fis = new FileInputStream (nuevo archivo (filePath)); datos de bytes [] = org.apache.commons.codec.digest.DigestUtils.md5 (fis); char md5Chars [] = Hex.encodeHex (datos); String md5 = String.valueOf (md5Chars); `
Dmitry_L

1
¡Agradable! Para proyectos nuevos, siempre lo pienso dos veces antes de agregar una nueva dependencia, pero para proyectos existentes solo tengo que verificar si la biblioteca ya está allí para usarla. +1
OscarRyz

164

Hay un ejemplo en Real-Java-How-to usando el MessageDigest clase .

Consulte esa página para ver ejemplos de uso de CRC32 y SHA-1 también.

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í ... todavía en línea después de 11 años! :-)
RealHowTo

El ejemplo de Real-Java-How-To funciona perfectamente y fue fácil de implementar.
bakoyaro

El ciclo de lectura es un poco torpe. read()no devolverá cero, y a do/whileno es realmente apropiado.
Marqués de Lorne

10
@EJP Gracias por sus comentarios oportunos.
Bill the Lizard

byte [] buffer = nuevo byte [1024]; ¿Podemos cambiar el tamaño de 1024 a algo más óptimo?
Jalpesh

90

La API com.google.common.hash ofrece:

  • Una API unificada y fácil de usar para todas las funciones hash
  • Implementaciones de 32 y 128 bits de murmur3 visibles
  • Los adaptadores md5 (), sha1 (), sha256 (), sha512 (), cambian solo una línea de código para cambiar entre estos y murmuran.
  • goodFastHash (int bits), para cuando no te importa qué algoritmo uses
  • Utilidades generales para instancias de HashCode, como combineOrdered / combineUnordered

Lea la Guía del usuario ( IO explicado , Hash explicado ).

Para su caso de uso Files.hash() calcula y devuelve el valor de resumen para un archivo.

Por ejemplo un Cálculo del resumen (cambie SHA-1 a MD5 para obtener el resumen MD5)

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

Tenga en cuenta que es mucho más rápido que , entonces usa si no necesita una suma de verificación criptográficamente segura. Tenga en cuenta también que no debe usarse para almacenar contraseñas y similares, ya que es fácil de aplicar fuerza bruta, para el uso de contraseñas , o en lugar.

Para la protección a largo plazo con hashes, un esquema de firma Merkle se suma a la seguridad y el Grupo de Estudio de Criptografía Post Quantum patrocinado por la Comisión Europea ha recomendado el uso de esta criptografía para la protección a largo plazo contra las computadoras cuánticas ( ref ).

Tenga en cuenta que tiene una tasa de colisión más alta que las demás.


¿Qué parte de Files.hash como se indicó anteriormente no cubre Files.hash?
Oluies

2
El Files.hash()está marcado como obsoleto, la forma recomendada es:Files.asByteSource(file).hash(Hashing.sha1())
erkfel

1
Y a partir de enero de 2018 Hashing.sha1()está marcado como obsoleto. La función Hashing.sha256()se recomienda en su lugar. fuente
MagicLegend

60

Usando nio2 (Java 7+) y sin bibliotecas externas:

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

Para comparar el resultado con una suma de verificación esperada:

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

@Arash sí, absolutamente, gracias. Mezclé la clase de archivos JDK y la de Guava.
Assylias

Esta solución me gusta más que la de Erickson, ya que puede envolverse con opciones para usar programación de estilo funcional puro
Gabriel Hernández

2
Para un archivo grande, esto usará mucha memoria ya que se lee todo el archivo y luego se alimenta al resumen en lugar de leer fragmentos y "digerirlos" a medida que se leen.
bernie

39

Guava ahora proporciona una nueva API de hashing coherente que es mucho más fácil de usar que las diversas API de hashing proporcionadas en el JDK. Ver Hashing explicado . Para un archivo, puede obtener fácilmente la suma MD5, CRC32 (con la versión 14.0+) o muchos otros hashes:

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

Okay. Tuve que agregar. Implementación de una línea para aquellos que ya tienen dependencia de Spring y Apache Commons o planean agregarla:

DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file))

Opción para los comunes comunes de Apache (crédito @duleshi):

DigestUtils.md5Hex(FileUtils.readFileToByteArray(file))

Espero que esto ayude a alguien.


1
EsDigestUtils.md5Hex(FileUtils.readFileToByteArray(file))
duleshi

La solución basada en los comunes de David Onter es mejor porque no lee un archivo completo en la memoria.
Fran Marzoa

Al menos Spring 5 , DigestUtils.md5Digest(InputStream inputStream)debe calcular el resumen MD5 y la DigestUtils.md5DigestAsHex(InputStream inputStream)representación de cadena hexadecimal de los métodos de resumen MD5 sin leer un archivo completo en la memoria.
Mike Shauneu

24

Un enfoque simple sin bibliotecas de terceros que utilizan Java 7

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

Si necesita imprimir esta matriz de bytes. Use como a continuación

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

Si necesita una cadena hexadecimal fuera de este resumen. Use como a continuación

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

donde DatatypeConverter es javax.xml.bind.DatatypeConverter


¿Por qué el toUpperCase?
EdgeCaseBerg

@edgecaseberg solo para que la cadena hexadecimal se vea bien mientras se imprime en la consola
sunil

Descubrí que necesitaba usar toLowerCase () en lugar de toUpperCase ().
Esplendor

14

Recientemente tuve que hacer esto solo para una cadena dinámica, MessageDigestpuede representar el hash de numerosas maneras. Para obtener la firma del archivo como lo haría con el comando md5sum , tuve que hacer algo como esto:

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

Obviamente, esto no responde a su pregunta sobre cómo hacerlo específicamente para un archivo, la respuesta anterior se ocupa muy bien de eso. Acabo de pasar mucho tiempo haciendo que la suma se vea como la mayoría de las aplicaciones lo muestran, y pensé que podría tener el mismo problema.


La firma es el resumen en formato hexadecimal. También encontré la representación hexadecimal para trabajar donde, como usted dice, otras representaciones no funcionan. Gracias por colgar esto.
amit

Esto es bueno, pero .toString(16)arrojará ceros a la izquierda. String.format("%032x", ...)quizás mejor.
Harold hace

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

O puede obtener más información http://www.asjava.com/core-java/java-md5-example/



9

Estábamos usando un código que se parece al código anterior en una publicación anterior usando

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

Sin embargo, BigInteger.toString()tenga cuidado al usar aquí, ya que truncará los ceros a la izquierda ... (por ejemplo, intente s = "27", la suma de verificación debería ser "02e74f10e0327ad868d138f2b4fdd6f0")

Secundo la sugerencia de usar Apache Commons Codec, reemplacé nuestro propio código con eso.


1
Wow, estaba investigando un problema en el que el material MD5 funcionaba perfectamente para todo, excepto que un archivo solo nos daba una salida de 31 dígitos hexadecimales y fallaba las sumas de md5. que truncar los ceros iniciales es un gran dolor ... Gracias por su 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

Método Java muy rápido y limpio que no se basa en bibliotecas externas:

(Simplemente reemplace MD5 con SHA-1, SHA-256, SHA-384 o SHA-512 si lo desea)

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

Forma estándar de Java Runtime Environment :

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

El resultado es igual a la utilidad linux md5sum.


6

Aquí hay una función simple que envuelve el código de Sunil para que tome un archivo como parámetro. La función no necesita ninguna biblioteca externa, pero sí requiere 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);            
    }


}

Salida de ejemplo:

hex=B117DD0C3CBBD009AC4EF65B6D75C97B

3

Si está utilizando ANT para construir, esto es muy simple. Agregue lo siguiente a su build.xml:

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

Donde jarFile es el JAR en el que desea generar el MD5 y toDir es el directorio donde desea colocar el archivo MD5.

Más información aquí.


3

Google guava ofrece una nueva API. Encuentra el siguiente:

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

Aquí hay una variación práctica que utiliza InputStream.transferTo()Java 9 y OutputStream.nullOutputStream()Java 11. No requiere bibliotecas externas y no necesita cargar todo el archivo en la 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()));
}

y

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

devoluciones

"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();
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.