Отримання контрольної суми MD5 файлу на Java


509

Я хочу використовувати Java, щоб отримати контрольну суму MD5 файлу. Я був дуже здивований, але мені не вдалося знайти нічого, що показує, як отримати контрольну суму MD5 файлу.

Як це робиться?


Можливо, це допоможе. Ви також можете шукати специфікацію, але це займе більше роботи, оскільки це складно.
waynecolvin

4
Майте на увазі, що згідно з останніми дослідженнями "MD5 слід вважати криптографічно розбитим і непридатним для подальшого використання". en.wikipedia.org/wiki/MD5
Захарія Стенлі

80
MD5 більше не вважається криптографічно захищеним, але його все ще достатньо для перевірки консистенції файлів і він швидший, ніж SHA.
джиггі

2
@ZakhariaStanley Це питання щодо контрольної суми.
iPherian

Канонічне використання контрольних сум MD5 для файлів полягає у униканні ворожої заміни розподілених файлів. Ось де це незахищено. Але в сценарії, коли ворожі подвиги не викликають особливих проблем, він цілком підходить.
Кіт Тайлер

Відповіді:


541

Є декоратор вхідного потоку java.security.DigestInputStream, так що ви можете обчислити дайджест, використовуючи вхідний потік, як зазвичай, замість того, щоб робити додатковий пропуск над даними.

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
Я погоджуюся, дуже елегантний спосіб обчислити контрольну суму на льоту, якщо ви вже робите щось із байтами (тобто читаєте їх із HTTP-з'єднання).
Марк Новаковський

2
@AlPhaba Ви оголосили це isяк InputStreamабо як FileInputStream? Звучить так, як ви раніше FileInputStream, що може спричинити цю помилку.
erickson

1
@barwnikk Це добре працює на Java 8. MethodNotFoundНе є винятком зі стандартної Java; можливо, ви говорите про помилку компілятора? У будь-якому випадку, якщо це не працює для вас, це локальна проблема конфігурації або проблема з іншим кодом.
еріксон

4
@barwnikk Знову ж таки, це ваша локальна проблема конфігурації. Це дійсний код Java 7 та Java 8. Якщо ви застрягли в інструментах з 2006 року, вам доведеться адаптуватися.
erickson

5
@erickson Ви не оновлюєте об'єкт MessageDigest вмістом файлу. Rt? Цей код надрукує завжди один і той же дайджест
суніль

302

Використовуйте DigestUtils з бібліотеки кодеків Apache Commons :

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

1
Не працює для мене в коді Android, я отримую цю помилку ... java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString на org.apache.commons.codec.digest.DigestUtils.md5Hex (DigestUtils.java:215)
JPM

@JPM Припускаємо, що ви вже завантажили і ставите commons-codec.jarна свій класний шлях ?
Лейф Грюнвольдт

так, і я експортував у своєму проекті Android. Я можу перейти через код, і клас є у вихідних файлах ... дивно, має бути проблема Android Eclipse.
JPM

1
У мене була така ж проблема, але вона виправлена ​​цим кодом `FileInputStream fis = new FileInputStream (новий файл (filePath)); дані байтів [] = org.apache.commons.codec.digest.DigestUtils.md5 (fis); char md5Chars [] = Hex.encodeHex (дані); String md5 = String.valueOf (md5Chars); `
Dmitry_L

1
Приємно! Щодо нових проектів я завжди думаю два рази, перш ніж додавати нову залежність, але для існуючого проекту я просто повинен перевірити, чи бібліотека вже є, щоб використовувати її. +1
OscarRyz

164

На прикладі Java-How-to для Real із класу MessageDigest є приклад .

Перевірте на цій сторінці приклади, використовуючи також CRC32 та 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
Так ... все ще в режимі он-лайн після 11 років! :-)
RealHowTo

Приклад в Real-Java-How-To від Real ідеально працює і був простий у виконанні.
бакояро

Цикл читання трохи незграбний. read()не поверне нуль, і do/whileсправді не підходить.
Маркіз Лорн

10
@EJP Дякуємо за своєчасний відгук.
Білл Ящірка

байт [] буфер = новий байт [1024]; чи можемо ми змінити розмір з 1024 на щось більш оптимальне?
Jalpesh

90

У com.google.common.hash API пропонує:

  • Уніфікований користувальницький API для всіх хеш-функцій
  • Насіннєві 32- та 128-бітні реалізації murmur3
  • Перехідники md5 (), sha1 (), sha256 (), sha512 (), змінюють лише один рядок коду для перемикання між цими, і шумить.
  • goodFastHash (int bits), коли вам не байдуже, який алгоритм ви використовуєте
  • Загальні утиліти для екземплярів HashCode, як, наприклад, комбінований / упорядкований

Прочитайте Керівництво користувача ( IO Роз'яснення , хешування Роз'яснення ).

Для ваших випадків використання Files.hash()обчислює і повертає дайджест значення для файлу.

Наприклад a підрахунок дайджесту (змініть SHA-1 на MD5, щоб отримати дайджест MD5)

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

Зауважте, що набагато швидше, ніж , тому використовуйте якщо вам не потрібна криптографічно захищена контрольна сума. Зауважте також, що не слід використовувати для зберігання паролів тощо, оскільки це легко піддавати грубій силі для використання паролів , або замість цього.

Для довготривалого захисту хешами схема підпису Меркле додає безпеку, а група досліджень квантової криптографії, спонсорована Європейською Комісією, рекомендує використовувати цю криптографію для довгострокового захисту від квантових комп'ютерів ( посилання ).

Зауважте, що має більш високу швидкість зіткнення, ніж інші.


Яка частина файлу.hash, як зазначено вище, не охоплює Files.hash?
олуї

2
Files.hash()Позначений як застарілий, рекомендованим способом є:Files.asByteSource(file).hash(Hashing.sha1())
erkfel

1
А станом на січень 2018 року Hashing.sha1()відзначається застарілим. Функція Hashing.sha256()рекомендується замість цього. джерело
MagicLegend

60

Використання nio2 (Java 7+) та відсутність зовнішніх бібліотек:

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

Щоб порівняти результат із очікуваною контрольною сумою:

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

@Arash так, абсолютно дякую. Я змішав клас файлів JDK і клас Guava.
ассілій

Мені це рішення подобається більше, ніж еріксону, оскільки його можна обгорнути додатковими функціями для програмування функціонального стилю
Габріель Ернандес,

2
Для великого файлу це буде використовувати багато пам'яті, оскільки весь файл читається, а потім подається на дайджест замість того, щоб читати шматки та "перетравлювати" їх під час читання.
Берні

39

Тепер Guava пропонує новий, послідовний API хешування, який набагато зручніший для користувачів, ніж різні хеширующие API, надані в JDK. Див. Роз'яснення хешингу . Для файлу ви можете легко отримати суму MD5, CRC32 (з версією 14.0+) або багато інших хешей:

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

Добре. Довелося додати. Реалізація одного рядка для тих, хто вже має залежність від Spring та Apache Commons або планує її додати:

DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file))

Для і лише Apache доступний варіант (кредит @duleshi):

DigestUtils.md5Hex(FileUtils.readFileToByteArray(file))

Сподіваюся, що це комусь допоможе.


1
ЦеDigestUtils.md5Hex(FileUtils.readFileToByteArray(file))
дулеші

Рішення на основі Девіда Онтера є кращим, оскільки воно не читає в пам'ять весь файл.
Fran Marzoa

По крайней мере, для Spring 5 вас DigestUtils.md5Digest(InputStream inputStream)слід обчислити дайджест MD5 і DigestUtils.md5DigestAsHex(InputStream inputStream)для шістнадцяткового представлення рядків методів перебору MD5 без зчитування цілого файлу в пам'яті.
Майк Шауне

24

Простий підхід без використання сторонніх бібліотек, що використовують Java 7

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

Якщо вам потрібно роздрукувати цей байтовий масив. Використовуйте як нижче

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

Якщо вам потрібна шестигранна рядок із цього дайджесту Використовуйте як нижче

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

де DatatypeConverter - javax.xml.bind.DatatypeConverter


Чому toUpperCase?
EdgeCaseBerg

@edgecaseberg просто для шестигранної струни добре виглядає під час друку на консолі
суніль

Я виявив, що мені потрібно використовувати toLowerCase () замість toUpperCase ().
Пишність

13

Нещодавно мені довелося це зробити лише для динамічної струни, MessageDigestможе представляти хеш у численних способах. Щоб отримати підпис файлу, як ви отримаєте за допомогою команди md5sum, я повинен був зробити щось на кшталт цього:

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

Це, очевидно, не відповідає на ваше запитання про те, як це зробити спеціально для файлу, вищевказана відповідь гарно відповідає. Я просто витратив багато часу на отримання суми, щоб виглядати як показ більшості додатків, і думав, що ти можеш зіткнутися з тією ж проблемою.


Підпис є дайджестом у шістнадцятковому форматі. Я теж знайшов шістнадцяткове представлення, щоб працювати там, де, як ви кажете, інші представлення не працюють. Дякую, що ви поставили це.
Аміт

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

Або ви можете отримати більше інформації http://www.asjava.com/core-java/java-md5-example/



9

Ми використовували код, який нагадує код вище в попередній публікації

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

Однак слідкуйте за використанням BigInteger.toString()тут, оскільки це вріже провідні нулі ... (наприклад, спробуйте s = "27", контрольна сума повинна бути "02e74f10e0327ad868d138f2b4fdd6f0")

Я вдруге пропоную використовувати кодек Apache Commons, я замінив власний код цим.


1
Нічого собі, я розглядав питання, в якому речі MD5 прекрасно працювали на все, за винятком того, що файл давав нам лише шістнадцятковий цифровий висновок і не вдавав md5checksums. що обрізання ведучих 0 - це величезний біль ... Дякую за вашу замітку.
Майк

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

Дуже швидкий і чистий Java-метод, який не покладається на зовнішні бібліотеки:

(Просто замініть MD5 на SHA-1, SHA-256, SHA-384 або SHA-512, якщо ви цього бажаєте)

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

Стандартний спосіб середовища виконання Java :

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

Результат дорівнює утиліті linux md5sum.


6

Ось проста функція, яка обертається навколо коду Sunil, щоб він взяв файл як параметр. Функція не потребує жодних зовнішніх бібліотек, але для цього потрібна 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);            
    }


}

Приклад виводу:

hex=B117DD0C3CBBD009AC4EF65B6D75C97B

3

Якщо ви використовуєте ANT для створення, це просто не просто. Додайте до свого build.xml наступне:

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

Там, де jarFile - це JAR, проти якого потрібно генерувати MD5, а toDir - каталог, у який потрібно розмістити файл MD5.

Більше інформації тут.


3

Google guava надає новий API. Знайдіть нижче:

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

Ось зручна версія, яка використовує як InputStream.transferTo()з Java 9, так і OutputStream.nullOutputStream()з Java 11. Для цього не потрібні зовнішні бібліотеки і не потрібно завантажувати весь файл у пам'ять.

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

і

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

повертає

"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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.