Java'da Dosya MD5 Sağlama Toplamı Alma


510

Bir dosyanın MD5 sağlama toplamı almak için Java kullanmak arıyorum. Gerçekten şaşırdım ama bir dosyanın MD5 sağlama toplamını nasıl alacağımı gösteren hiçbir şey bulamadım.

Nasıl oldu?


Belki bu yardımcı olacaktır. Ayrıca spec bakmak olabilir ama bu karmaşık olduğu için daha fazla şey yapmak gerekir.
08:49

4
Son araştırmalara göre "MD5'in kriptografik olarak kırılmış ve daha fazla kullanım için uygun olmadığı düşünülmelidir". en.wikipedia.org/wiki/MD5
Zakharia Stanley

80
MD5 artık kriptografik olarak güvenli kabul edilmiyor, ancak dosya tutarlılığını doğrulamak için hala yeterli ve SHA'dan daha hızlı.
jiggy

2
@ZakhariaStanley Bu sağlama toplamı ile ilgili bir soru.
iPherian

Dosyalarda MD5 sağlama toplamlarının standart kullanımı, dağıtılmış dosyaların düşmanca değiştirilmesini önlemektir. Güvenli olmayan yer burası. Ancak düşmanca istismarların endişe yaratmadığı bir senaryoda mükemmel bir şekilde uygundur.
Keith Tyler

Yanıtlar:


541

Bir giriş akışı dekoratörü vardır, java.security.DigestInputStreamböylece giriş akışını normalde yaptığınız gibi kullanırken, veri üzerinde ekstra bir geçiş yapmak yerine özeti hesaplayabilirsiniz.

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
Ben zaten bayt ile bir şey yapıyorsanız (yani bir HTTP bağlantısı üzerinden bunları okumak) anında sağlama toplamı hesaplamak için çok zarif bir yol katılıyorum.
Marc Novakowski

2
@AlPhaba the veya a isolarak mı ilan ettiniz ? Kullandığınız gibi geliyor , bu da bu hataya neden olacak. InputStreamFileInputStreamFileInputStream
erickson

1
@barwnikk Java 8'de iyi çalışır MethodNotFound. Standart Java'nın bir istisnası değildir; belki bir derleyici hatası hakkında konuşuyorsunuz? Her durumda, sizin için işe yaramazsa, bu bir yerel yapılandırma sorunu veya diğer kodla ilgili bir sorundur.
erickson

4
@barwnikk Yine, bu yerel yapılandırma sorununuz. Bu geçerli bir Java 7 ve Java 8 kodudur. 2006'dan itibaren araçlarla sıkışıp kalırsanız uyum sağlamanız gerekir.
erickson

5
@erickson MessageDigest nesnesini dosya içeriğiyle güncelleştirmiyorsunuz. Rt? Bu kod her zaman aynı özeti basacaktır.
sunil

302

Kullanım DigestUtils gelen Apache Commons Codec kütüphanesinde:

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

1
Benim android kod benim için çalışmıyor Bu hatayı alıyorum ... java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString org.apache.commons.codec.digest.DigestUtils.md5Hex (DigestUtils.java:215)
JPM

@JPM İndirdiğinizi ve commons-codec.jarzaten sınıf yolunuza koyduğunuzu varsayın ?
Leif Gruenwoldt

evet orada ve ben benim android projede ihraç .. Ben kod üzerinden adım ve sınıf kaynak dosyalarında ... garip, bazı android Eclipse sorunu olmalı.
JPM

1
Aynı sorunu vardı, ancak bu kod ile düzeltildi `FileInputStream fis = new FileInputStream (yeni File (filePath)); bayt verileri [] = org.apache.commons.codec.digest.DigestUtils.md5 (fis); char md5Chars [] = Hex.encodeHex (veri); String md5 = String.valueOf (md5Chars); ``
Dmitry_L

1
Güzel! Yeni projeler için her zaman yeni bir bağımlılık eklemeden önce iki kez düşünürüm, ancak mevcut proje için kütüphanenin zaten kullanmak için orada olup olmadığını kontrol etmeliyim. +1
OscarRyz

164

Real'in Java-How- to'da MessageDigest sınıfını kullanma örneği var .

CRC32 ve SHA-1 kullanan örnekler için bu sayfayı kontrol edin.

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
Evet ... 11 sene sonra hala çevrimiçi! :-)
RealHowTo

Real Java-How-To'daki örnek mükemmel çalışıyor ve uygulaması basitti.
bakoyaro

Okuma döngüsü biraz sakar. read()sıfır döndürmez ve do/whilegerçekten uygun değildir.
Lorne Marquis,

10
@EJP Zamanında geri bildiriminiz için teşekkür ederiz.
Kertenkele Bill

bayt [] tampon = yeni bayt [1024]; boyutu 1024'ten daha uygun bir şeye değiştirebilir miyiz?
Jalpesh

90

Com.google.common.hash API sunar:

  • Tüm karma işlevler için birleşik kullanıcı dostu bir API
  • Görülebilen 32 ve 128 bit üfürüm uygulamaları3
  • md5 (), sha1 (), sha256 (), sha512 () adaptörleri, bunlar ve üfürüm arasında geçiş yapmak için yalnızca bir kod satırını değiştirin.
  • goodFastHash (int bit), hangi algoritmayı kullandığınız umurumda değilken
  • CombineOrdered / combineUnordered gibi HashCode örnekleri için genel yardımcı programlar

Kullanım Kılavuzunu okuyun ( IO Açıklaması , Hashing Açıklaması ).

Kullanım durumunuz Files.hash()için bir dosyanın özet değerini hesaplar ve döndürür.

Örneğin a özet hesaplaması (MD5 özetini almak için SHA-1'i MD5 olarak değiştirin)

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

Bunu not et daha hızlı , öyleyse kullan kriptografik olarak güvenli bir sağlama toplamına ihtiyacınız yoksa. Ayrıca şunu da unutmayın şifreleri saklamak için kullanılmamalıdır, çünkü şifreleri kullanmak için gücü zorlaştırmak kolaydır , veya yerine.

Karmalarla uzun süreli koruma için, güvenliğe bir Merkle imza şeması eklenir ve Avrupa Komisyonu tarafından desteklenen Post Kuantum Kriptografi Çalışma Grubu, bu kriptografinin kuantum bilgisayarlara karşı uzun süreli koruma için kullanılmasını tavsiye etmiştir ( ref ).

Bunu not et çarpışma oranı diğerlerine göre daha yüksektir.


Yukarıda belirtildiği gibi Files.hash'ın hangi kısmı Files.hash'ı kapsamaz?
oluies

2
Files.hash()Kullanımdan kaldırıldı olarak işaretlenmiş, tavsiye yoludur:Files.asByteSource(file).hash(Hashing.sha1())
erkfel

1
Ocak 2018'den itibaren Hashing.sha1()kullanımdan kaldırıldı. Bunun Hashing.sha256()yerine işlev önerilir. kaynağı
MagicLegend

60

Nio2 (Java 7+) kullanma ve harici kitaplık kullanma:

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

Sonucu beklenen bir sağlama toplamıyla karşılaştırmak için:

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

@Arash evet kesinlikle - teşekkürler. JDK Files sınıfını ve Guava sınıflarını karıştırdım.
assylias

Bu işlevi erickson'dan daha çok seviyorum çünkü saf Fonksiyonel stil programlama kullanmak için Optionals ile paketlenebilir
Gabriel Hernandez

2
Büyük bir dosya için, tüm dosya okunduğundan ve daha sonra parçaları okumak ve okunduğu gibi "sindirmek" yerine sindirime beslendiği için bu çok fazla bellek kullanır.
bernie

39

Guava şimdi, JDK'da sağlanan çeşitli karma API'lerden çok daha kullanıcı dostu olan yeni, tutarlı bir karma karma API sağlıyor. Bkz. Hashing Açıklaması . Bir dosya için MD5 toplamı, CRC32 (sürüm 14.0+ ile) veya diğer birçok karmayı kolayca alabilirsiniz:

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

Tamam. Eklemek zorundaydım. Zaten Spring ve Apache Commons bağımlılığı olan veya eklemeyi planlayanlar için bir satır uygulama:

DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file))

Yalnızca ve Apache ortak seçeneği (credit @duleshi):

DigestUtils.md5Hex(FileUtils.readFileToByteArray(file))

Umarım bu birine yardımcı olur.


1
It'sDigestUtils.md5Hex(FileUtils.readFileToByteArray(file))
duleshi

David Onter müşterek tabanlı bir çözüm daha iyidir çünkü tüm dosyayı belleğe okumaz.
Fran Marzoa

İçin en azından Spring 5 size sahip DigestUtils.md5Digest(InputStream inputStream)MD5 ve hesaplamak için DigestUtils.md5DigestAsHex(InputStream inputStream)belleğe bir bütün dosyayı okumak olmadan MD5 özeti yöntemlerden onaltılık dize gösterimi için.
Mike Shauneu

24

Java 7 kullanan üçüncü taraf kitaplıkları olmayan basit bir yaklaşım

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

Bu bayt dizisini yazdırmanız gerekiyorsa. Aşağıdaki gibi kullanın

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

Bu özetten hex dizgeye ihtiyacınız varsa. Aşağıdaki gibi kullanın

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

burada DatatypeConverter javax.xml.bind.DatatypeConverter


Neden toUpperCase?
EdgeCaseBerg

@edgecaseberg sadece altıgen dize için konsola yazdırırken iyi görünüyor
sunil

Ben toUpperCase () yerine toLowerCase () kullanmak için gerekli bulundu.
Splendor

14

Son zamanlarda bunu sadece dinamik bir dize için yapmak zorunda kaldım MessageDigest, hash'ı çeşitli şekillerde temsil edebilir. Md5sum komutu ile alacağınız gibi dosyanın imzasını almak için böyle bir şey yapmak zorunda kaldım:

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

Bu açıkça bir dosya için özel olarak nasıl yapılacağı ile ilgili sorunuza cevap vermez, yukarıdaki cevap bu sessizle güzelce ilgilenir. Sadece çoğu uygulamanın görüntülediği gibi görünmesi için çok zaman harcadım ve aynı sorunla karşılaşabileceğinizi düşündüm.


İmza onaltılık formattaki özettir. Ben de onaltılık temsili, söylediğiniz gibi, diğer temsillerin çalışmadığı yerde buldum. Bunu eklediğin için teşekkürler.
099 amit

Bu iyi, ama .toString(16)baştaki sıfırları atacak. String.format("%032x", ...)belki daha iyi.
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());
}

Veya daha fazla bilgi alabilirsiniz http://www.asjava.com/core-java/java-md5-example/



9

Önceki bir yazıda yukarıdaki koda benzeyen bir kod kullanıyorduk

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

Ancak, BigInteger.toString()önde gelen sıfırları keseceği için burada kullanmaya dikkat edin ... (örneğin, deneyin s = "27", sağlama toplamı olmalıdır "02e74f10e0327ad868d138f2b4fdd6f0")

Apache Commons Codec'i kullanma önerisini ikinci olarak, kendi kodumuzu bununla değiştirdim.


1
Wow, MD5 şeyler her şey için mükemmel çalıştığı bir sorun arıyordum, bir dosya bize sadece 31 hex haneli çıkış veriyordu ve md5checksums başarısız oldu. 0'ların başının kesilmesi büyük bir acıdır ... Notunuz için teşekkürler.
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

Harici kütüphanelere dayanmayan çok hızlı ve temiz Java yöntemi:

(İsterseniz MD5'i SHA-1, SHA-256, SHA-384 veya SHA-512 ile değiştirmeniz yeterlidir)

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

Standart Java Çalışma Zamanı Ortamı yolu :

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

Sonuç linux md5sum yardımcı programına eşittir.


6

Sunil kodunu saran basit bir işlevdir, böylece bir dosyayı parametre olarak alır. İşlev herhangi bir harici kütüphaneye ihtiyaç duymaz, ancak Java 7 gerektirir.

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


}

Örnek çıktı:

hex=B117DD0C3CBBD009AC4EF65B6D75C97B

3

Eğer inşa etmek için ANT kullanıyorsanız, bu son derece basittir. Build.xml dosyanıza aşağıdakileri ekleyin:

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

JarFile, MD5'i oluşturmak istediğiniz JAR ve toDir, MD5 dosyasını yerleştirmek istediğiniz dizindir.

Daha fazla bilgi burada.


3

Google guava yeni bir API sunuyor. Aşağıdakini bulun:

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

İşte InputStream.transferTo()Java 9 ve OutputStream.nullOutputStream()Java 11'den yararlanan kullanışlı bir varyasyon. Harici kitaplık gerektirmez ve tüm dosyayı belleğe yüklemeye gerek yoktur.

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

ve

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

İadeler

"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();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.