Получение контрольной суммы MD5 в Java

510

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

Как это сделать?

Джек
источник
Может быть, это поможет. Вы также можете посмотреть спецификации, но это займет больше времени, потому что это сложно.
Уэйнколвин
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, что приведет к этой ошибке.
Эриксон
1
@barwnikk Отлично работает в Java 8. MethodNotFoundНе является исключением из стандартной Java; возможно вы говорите об ошибке компилятора? В любом случае, если это не работает для вас, это проблема локальной конфигурации или проблема с другим кодом.
Эриксон
4
@barwnikk Опять же, это ваша локальная проблема конфигурации. Это действительный код Java 7 и Java 8. Если вы застряли с инструментами с 2006 года, вам придется адаптироваться.
Эриксон
5
@erickson Вы не обновляете объект MessageDigest содержимым файла. Рт? Этот код будет печатать всегда один и тот же дайджест.
Сунил
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 at org.apache.commons.codec.digest.DigestUtils.md5Hex (DigestUtils.java:215)
JPM
@JPM Предположим, что вы уже загрузили и поместили в commons-codec.jarсвой путь к классам?
Лейф Грюнволдт
да, и я экспортировал в свой проект Android .. Я могу пройти через код и класс есть в исходных файлах ... странно, должно быть, некоторые проблемы Android Eclipse.
JPM
1
У меня была та же проблема, но она была исправлена ​​этим кодом `FileInputStream fis = new FileInputStream (new File (filePath)); байтовые данные [] = org.apache.commons.codec.digest.DigestUtils.md5 (fis); char md5Chars [] = Hex.encodeHex (data); 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
Пример в Java-How-To от Real отлично работает и прост в реализации.
Bakoyaro
Цикл чтения немного неуклюж. read()не вернет ноль, и do/whileне очень подходит.
Маркиз Лорн
10
@EJP Спасибо за ваш своевременный отзыв.
Билл Ящерица
byte [] buffer = новый байт [1024]; мы можем изменить размер с 1024 на что-то более оптимальное?
Jalpesh
90

В com.google.common.hash API предлагает:

  • Унифицированный удобный API для всех хеш-функций
  • Seedable 32- и 128-битные реализации murmur3
  • Адаптеры md5 (), sha1 (), sha256 (), sha512 (), изменяют только одну строку кода для переключения между ними и бормотают.
  • goodFastHash (int bits), когда вам все равно, какой алгоритм вы используете
  • Общие утилиты для экземпляров HashCode, такие как combOrdered / combUnordered

Прочитайте Руководство пользователя ( IO Разъяснения , хеширование Разъяснения ).

Для вашего Files.hash()варианта использования вычисляет и возвращает значение дайджеста для файла.

Например, расчет дайджеста (измените SHA-1 на MD5, чтобы получить дайджест MD5)

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

Обратите внимание, что намного быстрее чем так что пользуйтесь если вам не нужна криптографически безопасная контрольная сумма. Обратите внимание, что не следует использовать для хранения паролей и тому подобного, так как это просто для грубой силы, для использования паролей , или вместо.

Для долгосрочной защиты с помощью хэшей схема подписи Merkle повышает безопасность, а Исследовательская группа по постквантовой криптографии, спонсируемая Европейской комиссией, рекомендовала использовать эту криптографию для долговременной защиты от квантовых компьютеров ( см. ).

Обратите внимание, что имеет более высокий уровень столкновений, чем другие.

oluies
источник
Какая часть Files.hash, как указано выше, не покрывает Files.hash?
oluies
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");
assylias
источник
@ Араш, да, конечно, спасибо. Я перепутал класс JDK Files и Guava.
assylias
Мне нравится это решение больше, чем решение erickson, так как оно может быть обернуто с помощью Optionals для использования чисто функционального программирования
Габриэль Эрнандес,
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();
ColinD
источник
32

Хорошо. Я должен был добавить. Однострочная реализация для тех, кто уже имеет зависимость от Spring и Apache Commons или планирует добавить ее:

DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file))

Параметр «Только для общего доступа» и Apache (credit @duleshi):

DigestUtils.md5Hex(FileUtils.readFileToByteArray(file))

Надеюсь, это кому-нибудь поможет.

MickJ
источник
1
ЭтоDigestUtils.md5Hex(FileUtils.readFileToByteArray(file))
дулеши
Лучше решение, основанное на общедоступных файлах David Onter, поскольку оно не считывает весь файл в память.
Фран Марсоа
По крайней мере, 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 только для шестнадцатеричной строки хорошо смотрится при печати на консоли
sunil
Я обнаружил, что мне нужно использовать toLowerCase () вместо toUpperCase ().
Великолепие
14

Мне недавно пришлось сделать это только для динамической строки, которая 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();
}

Это, очевидно, не отвечает на ваш вопрос о том, как сделать это специально для файла, приведенный выше ответ прекрасно с этим справляется. Я просто потратил много времени на то, чтобы сумма выглядела так, как показывает большинство приложений, и подумал, что вы можете столкнуться с той же проблемой.

Брайан Джанфоркаро
источник
Подпись представляет собой дайджест в шестнадцатеричном формате. Я также нашел шестнадцатеричное представление, чтобы работать там, где, как вы говорите, другие представления не работают. Спасибо, что подняли это.
Амит
Это хорошо, но .toString(16)выбросит ведущие нули. String.format("%032x", ...)может быть лучше.
Гарольд
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 checksum = DigestUtils.md5Hex(new FileInputStream(filePath));
Равикиран Калал
источник
9

Мы использовали код, который похож на код выше в предыдущем посте, используя

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

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

Второе предложение использовать кодек Apache Commons, я заменил наш собственный код на него.

user552999
источник
1
Ух ты, я искал проблему, в которой материал MD5 работал идеально для всего, кроме файла, который давал нам только 31 шестнадцатеричный вывод, и не выполнял md5checksums. урезание ведущих нулей - огромная боль ... Спасибо за ваше замечание.
Майк
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
источник
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 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;
  }
}

Результат равен утилите linux md5sum.

gotozero
источник
6

Вот простая функция, которая оборачивает код Sunil, так что он принимает File в качестве параметра. Функция не нуждается во внешних библиотеках, но требует 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
stackoverflowuser2010
источник
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();
}
XXX
источник