Nhận tổng kiểm tra MD5 của tệp trong Java


510

Tôi đang tìm cách sử dụng Java để lấy tổng kiểm MD5 của một tệp. Tôi thực sự ngạc nhiên nhưng tôi không thể tìm thấy bất cứ điều gì chỉ ra cách lấy tổng kiểm tra MD5 của một tệp.

Làm thế nào được thực hiện?


Có lẽ điều này sẽ giúp. Bạn cũng có thể tra cứu thông số kỹ thuật nhưng sẽ mất nhiều công sức hơn vì nó phức tạp.
waynecolvin

4
Hãy nhớ rằng theo nghiên cứu gần đây "MD5 nên được coi là bị phá vỡ bằng mật mã và không phù hợp để sử dụng tiếp". vi.wikipedia.org/wiki/MD5
Zakharia Stanley

80
MD5 không còn được coi là bảo mật bằng mật mã, nhưng nó vẫn đủ để xác thực tính nhất quán của tệp và nó nhanh hơn SHA.
nhảy lên

2
@ZakhariaStanley Đây là một câu hỏi về kiểm tra.
iPherian

Việc sử dụng hợp quy cho tổng kiểm MD5 trên các tệp là để tránh sự thay thế thù địch của các tệp phân tán. Đó là nơi không an toàn. Nhưng trong một kịch bản mà việc khai thác thù địch không phải là một mối quan tâm, nó hoàn toàn phù hợp.
Keith Tyler

Câu trả lời:


541

Có một trình trang trí luồng đầu vào java.security.DigestInputStream, để bạn có thể tính toán thông báo trong khi sử dụng luồng đầu vào như bình thường, thay vì phải truyền thêm dữ liệu.

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
Tôi đồng ý, một cách rất thanh lịch để tính toán tổng kiểm tra nếu bạn đang làm gì đó với các byte (tức là đọc chúng từ kết nối HTTP).
Marc Novakowski

2
@AlPhaba Bạn đã khai báo islà một InputStreamhay a FileInputStream? Âm thanh như bạn đã sử dụng FileInputStream, sẽ gây ra lỗi này.
erickson

1
@barwnikk Nó hoạt động tốt trong Java 8. MethodNotFoundkhông phải là ngoại lệ so với Java tiêu chuẩn; có lẽ bạn đang nói về một lỗi biên dịch? Trong mọi trường hợp, nếu nó không hoạt động với bạn, thì đó là sự cố cấu hình cục bộ hoặc sự cố với mã khác.
erickson

4
@barwnikk Một lần nữa, đó là vấn đề cấu hình cục bộ của bạn. Đây là mã Java 7 và Java 8 hợp lệ. Nếu bạn bị mắc kẹt với các công cụ từ năm 2006, bạn sẽ phải thích nghi.
erickson

5
@erickson Bạn không cập nhật đối tượng MessageDigest với nội dung tệp. Rt? Mã này sẽ luôn luôn in một bản tóm tắt.
Sunil

302

Sử dụng DigestUtils từ thư viện Codec Apache Commons :

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

1
Không hoạt động với mã Android của tôi, tôi gặp lỗi này ... java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString tại org.apache.commons.codec.digest.DigestUtils.md5 (DigestUtils.java:215)
JPM

@JPM Giả sử bạn đã tải xuống và đưa commons-codec.jarvào classpath của bạn rồi?
Leif Gruenwoldt

vâng, tôi đã xuất trong dự án Android của mình..Tôi có thể bước qua mã và lớp có trong các tệp nguồn ... kỳ lạ, phải là một số vấn đề về Eclipse của Android.
JPM

1
Tôi đã có cùng một vấn đề, nhưng nó đã được sửa bởi mã này `FileInputStream fis = new FileInputStream (Tệp mới (filePath)); dữ liệu byte [] = org.apache.commons.codec.digest.DigestUtils.md5 (fis); char md5Chars [] = Hex.encodeHex (dữ liệu); Chuỗi md5 = String.valueOf (md5Chars); `
Dmitry_L

1
Đẹp! Đối với các dự án mới, tôi luôn nghĩ hai lần trước khi thêm một phụ thuộc mới nhưng đối với dự án hiện tại tôi chỉ cần kiểm tra xem thư viện đã có sẵn để sử dụng chưa. +1
OscarRyz

164

Có một ví dụ tại Java - Cách sử dụng MessageDigest của Real lớp .

Kiểm tra trang đó để biết ví dụ sử dụng CRC32 và 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
Đúng ... vẫn trực tuyến sau 11 năm! :-)
RealHowTo

Ví dụ tại Java-How-To của Real hoạt động hoàn hảo và rất đơn giản để thực hiện.
bakoyaro

Vòng lặp đọc là một chút vụng về. read()sẽ không trả về số 0 và do/whilekhông thực sự phù hợp.
Hầu tước Lorne

10
@EJP Cảm ơn phản hồi kịp thời của bạn.
Bill Lizard

byte [] buffer = new byte [1024]; chúng ta có thể thay đổi kích thước từ 1024 thành một cái gì đó tối ưu hơn không?
Jalpesh

90

Các com.google.common.hash Mời API:

  • API thân thiện với người dùng thống nhất cho tất cả các hàm băm
  • Có thể triển khai 32 và 128 bit của murmur3
  • bộ điều hợp md5 (), sha1 (), sha256 (), sha512 (), chỉ thay đổi một dòng mã để chuyển đổi giữa những điều này và tiếng thì thầm.
  • goodFastHash (int bit), khi bạn không quan tâm bạn sử dụng thuật toán nào
  • Các tiện ích chung cho các trường hợp HashCode, như kết hợp / Kết hợp / sắp xếp

Đọc Hướng dẫn sử dụng ( Giải thích IO , Giải thích băm ).

Đối với trường hợp sử dụng của bạn Files.hash()tính toán và trả về giá trị tiêu hóa cho một tệp.

Ví dụ tính toán tiêu hóa (thay đổi SHA-1 thành MD5 để nhận thông báo MD5)

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

Lưu ý rằng nhanh hơn nhiều , nên sử dụng nếu bạn không cần một tổng kiểm tra an toàn bằng mật mã. Cũng lưu ý rằng không nên được sử dụng để lưu trữ mật khẩu và những thứ tương tự vì nó dễ dàng để vũ trang, để sử dụng mật khẩu , hoặc là thay thế.

Để bảo vệ lâu dài với băm, sơ đồ chữ ký Merkle tăng thêm tính bảo mật và Nhóm Nghiên cứu Mật mã học Lượng tử do Ủy ban Châu Âu tài trợ đã khuyến nghị sử dụng mật mã này để bảo vệ lâu dài đối với máy tính lượng tử ( ref ).

Lưu ý rằng có tỷ lệ va chạm cao hơn những người khác.


Phần nào của Files.hash như đã nêu ở trên không bao gồm Files.hash?
oluies

2
Các Files.hash()được đánh dấu là bị phản đối, cách đề nghị là:Files.asByteSource(file).hash(Hashing.sha1())
erkfel

1
Và kể từ tháng 1 năm 2018 Hashing.sha1()được đánh dấu không dùng nữa. Các chức năng Hashing.sha256()được đề nghị thay thế. nguồn
MagicLegend 20/03/18

60

Sử dụng nio2 (Java 7+) và không có thư viện bên ngoài:

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

Để so sánh kết quả với tổng kiểm tra dự kiến:

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

@Arash có hoàn toàn - cảm ơn. Tôi đã trộn lẫn lớp JDK Files và Guava's.
assylias

Tôi thích giải pháp này hơn erickson vì nó có thể được gói bằng Tùy chọn để sử dụng lập trình theo kiểu Chức năng thuần túy
Gabriel Hernandez

2
Đối với một tệp lớn, điều này sẽ sử dụng rất nhiều bộ nhớ vì toàn bộ tệp được đọc và sau đó được đưa vào thông báo thay vì đọc các đoạn và "tiêu hóa" chúng khi chúng được đọc.
bernie

39

Guava hiện cung cấp API băm mới, phù hợp, thân thiện với người dùng hơn nhiều so với các API băm khác nhau được cung cấp trong JDK. Xem Băm giải thích . Đối với một tệp, bạn có thể nhận được tổng MD5, CRC32 (với phiên bản 14.0+) hoặc nhiều giá trị băm khác:

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

Đồng ý. Tôi đã phải thêm. Triển khai một dòng cho những người đã có sự phụ thuộc của Spring và Apache Commons hoặc đang có kế hoạch thêm nó:

DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file))

Tùy chọn chỉ dành cho và commons Apache (credit @duleshi):

DigestUtils.md5Hex(FileUtils.readFileToByteArray(file))

Hy vọng điều này sẽ giúp được ai đó.


1
Đó làDigestUtils.md5Hex(FileUtils.readFileToByteArray(file))
duleshi

Giải pháp dựa trên commons của David Onter là tốt hơn bởi vì nó không đọc toàn bộ tệp vào bộ nhớ.
Fran Marzoa

Ít nhất là đối với Spring 5 bạn phải DigestUtils.md5Digest(InputStream inputStream)tính toán tiêu hóa MD5 và DigestUtils.md5DigestAsHex(InputStream inputStream)biểu diễn chuỗi thập lục phân của các phương thức phân loại MD5 mà không đọc toàn bộ tệp vào bộ nhớ.
Mike Shauneu

24

Một cách tiếp cận đơn giản không có thư viện bên thứ ba sử dụng Java 7

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

Nếu bạn cần in mảng byte này. Sử dụng như dưới đây

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

Nếu bạn cần chuỗi hex trong số này. Sử dụng như dưới đây

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

trong đó DatatypeConverter là javax.xml.bind.DatatypeConverter


Tại sao toUpperCase?
EdgeCaseBerg

@edgecaseberg chỉ dành cho chuỗi hex trông đẹp khi in nó lên bàn điều khiển
sunil

Tôi thấy tôi cần sử dụng toLowerCase () thay vì toUpperCase ().
Lộng lẫy

14

Gần đây tôi đã phải làm điều này chỉ vì một chuỗi động, MessageDigestcó thể đại diện cho hàm băm theo nhiều cách. Để có được chữ ký của tập tin như bạn sẽ nhận được bằng lệnh md5sum, tôi đã phải làm một cái gì đó như thế này:

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

Điều này rõ ràng không trả lời câu hỏi của bạn về cách thực hiện cụ thể cho một tệp, câu trả lời ở trên xử lý sự yên tĩnh đó. Tôi chỉ mất rất nhiều thời gian để có được số tiền trông giống như hầu hết các ứng dụng hiển thị nó và nghĩ rằng bạn có thể gặp rắc rối tương tự.


Chữ ký là bản tóm tắt ở định dạng thập lục phân. Tôi cũng tìm thấy đại diện thập lục phân để làm việc trong đó, như bạn nói, các đại diện khác không hoạt động. Cảm ơn cho sự lưu tâm.
amit

Điều này là tốt, nhưng .toString(16)sẽ vứt bỏ số không hàng đầu. String.format("%032x", ...)có thể tốt hơn.
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());
}

Hoặc bạn có thể biết thêm thông tin http://www.asjava.com/core-java/java-md5-example/



9

Chúng tôi đã sử dụng mã giống với mã ở trên trong bài viết trước bằng cách sử dụng

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

Tuy nhiên, coi chừng sử dụng BigInteger.toString()ở đây, vì nó sẽ cắt bớt các số 0 đứng đầu ... (ví dụ: thử s = "27", tổng kiểm tra phải là"02e74f10e0327ad868d138f2b4fdd6f0" )

Tôi thứ hai gợi ý sử dụng Apache Commons Codec, tôi đã thay thế mã của chúng tôi bằng mã đó.


1
Ồ, tôi đã xem xét một vấn đề trong đó công cụ MD5 hoạt động hoàn hảo cho mọi thứ, ngoại trừ một tệp chỉ cung cấp cho chúng tôi một đầu ra 31 chữ số hex và đã thất bại với md5checksums. việc cắt giảm số 0 đứng đầu là một nỗi đau rất lớn ... Cảm ơn bạn đã lưu ý.
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

Phương thức Java rất nhanh và sạch mà không phụ thuộc vào các thư viện bên ngoài:

(Chỉ cần thay MD5 bằng SHA-1, SHA-256, SHA-384 hoặc SHA-512 nếu bạn muốn

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

Cách môi trường thời gian chạy Java tiêu chuẩn :

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

Kết quả là bằng tiện ích linux md5sum.


6

Đây là một hàm đơn giản bao bọc mã của Sunil để nó lấy Tệp làm tham số. Hàm này không cần bất kỳ thư viện bên ngoài nào, nhưng nó yêu cầu 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);            
    }


}

Ví dụ đầu ra:

hex=B117DD0C3CBBD009AC4EF65B6D75C97B

3

Nếu bạn đang sử dụng ANT để xây dựng, việc này rất đơn giản. Thêm phần sau vào tệp build.xml của bạn:

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

Trong đó jarFile là JAR mà bạn muốn tạo MD5 và toDir là thư mục bạn muốn đặt tệp MD5.

Thêm thông tin ở đây.


3

Quả ổi của Google cung cấp một API mới. Tìm cái dưới đây:

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

Đây là một biến thể tiện dụng sử dụng InputStream.transferTo()từ Java 9 và OutputStream.nullOutputStream()từ Java 11. Nó không yêu cầu các thư viện bên ngoài và không cần phải tải toàn bộ tệp vào bộ nhớ.

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

trả lại

"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();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.