計算檔案Checksum的幾種方法

dunne21發表於2021-09-09

    回憶一下,自己是否在網站上下載檔案時看到過Checksum這個東西,一串字串?

    比如,我們到Apache網站上去下載用於操作Excel的依賴包 - Apache POI,就可以看到checksum:SHA-256, SHA-512, 如下圖所示:


    以poi-bin-4.1.0-20190412.tar.gz檔案為例,點選SHA-256和SHA-512的連結檢視相關的值如下:


##SHA-256的值d8db4f8228d87935ca46b0af72db68ad83f45b31d885e67b089d195b5ee800bb

##SHA-512的值
87499ab94882605ee2f407fc66e24c613ae98896b8d5f527b6cd8c604574922fc72d148da42962b2ee30ad18cd712e3de42bfe14770261b07217717c52a738a9

    本文將簡單介紹一下checksum(含義,作用)以及如何使用java程式計算出不同演算法的checksum值,包括MD5、SHA-1,SHA-256以及SHA-512。

Checksum:總和檢驗碼,校驗和。在資料處理和資料通訊領域中,用於校驗目的的一組資料項的和。這些資料項可
以是數字或在計算檢驗總和過程中看作數字的其它字串。通常是以十六進位制為數製表示的形式。
【作用】就是用於檢查檔案完整性,檢測檔案是否被惡意篡改,比如檔案傳輸(如外掛、韌體升級包等)場景使用。

    接下來,我們一起看下怎麼使用java程式產生相關的checksum值,本文以檔案poi-bin-4.1.0-20190412.tar.gz為例,具體可以透過如下路徑下載:


因為要使用不同演算法的checksum值,包括MD5、SHA-1,SHA-256以及SHA-512,先定義一個列舉類,用於區分不同的演算法。

package com.wangmengjun.tutorial.checksum;

public enum CheckSumAlgoType {
  MD5("MD5"), SHA_256("SHA-256"), SHA_512("SHA-512"), SHA_1("SHA1"); 
  private String name;
  private CheckSumAlgoType(String name) {    
      this.name = name;  
  }
  public String getName() {    return name;  }
  public void setName(String name) {    this.name = name;  }
}


    接下來,我們就來看看幾種計算檔案checksum的方法:

  1. 使用java.security.MessageDigest

  2. 使用org.apache.commons.codec.digest.DigestUtils

  3. 使用com.google.common.io.Files.hash

一、使用java.security.MessageDigest

public static String genChecksum1(File file, String checkSumAlgo) throws NoSuchAlgorithmException, IOException {    
    MessageDigest messageDigest = MessageDigest.getInstance(checkSumAlgo);    
    messageDigest.update(Files.readAllBytes(file.toPath()));    
    byte[] digestBytes = messageDigest.digest();    
    StringBuffer sb = new StringBuffer();    
    for (byte b : digestBytes) {      
        sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));    
    }    
    return sb.toString();  
}

    其中,下面的這段程式碼,

StringBuffer sb = new StringBuffer();    
for (byte b : digestBytes) {      
    sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));    
}    
return sb.toString();

    可以使用javax.xml.bind.DatatypeConverter的方法來做,簡化後的程式碼如下:

public static String genChecksum1(File file, String checkSumAlgo) throws NoSuchAlgorithmException, IOException {    
        MessageDigest messageDigest = MessageDigest.getInstance(checkSumAlgo);    
        messageDigest.update(Files.readAllBytes(file.toPath()));    
        byte[] digestBytes = messageDigest.digest();    
        return DatatypeConverter.printHexBinary(digestBytes).toLowerCase();  
}
    因為,DatatypeConverter.printHexBinary(digestBytes)返回的字元大寫,所以新增了toLowerCase()方法保持其一致性。


二、使用org.apache.commons.codec.digest.DigestUtils



    使用commons-codec來完成,Maven工程需要新增依賴包,如:

<!--  -->
<dependency>    
    <groupId>commons-codec</groupId>    
    <artifactId>commons-codec</artifactId>    
    <version>1.13</version>
</dependency>

    簡單程式碼如下,透過呼叫類DigestUtils的靜態方法完成指定checksum的計算即可:  

public static String genChecksum2(File file, CheckSumAlgoType checkSumAlgoType)      throws FileNotFoundException, IOException {    
/**     * 使用org.apache.commons.codec.digest.DigestUtils     */    
        String checksum = null;    
        switch (checkSumAlgoType) {    
            case MD5:      
                checksum = DigestUtils.md5Hex(new FileInputStream(file));      
                break;
            case SHA_1:      
                checksum = DigestUtils.sha1Hex(new FileInputStream(file));      
                break;
            case SHA_256:      
                checksum = DigestUtils.sha256Hex(new FileInputStream(file));      
                break;    
            case SHA_512:      
                checksum = DigestUtils.sha512Hex(new FileInputStream(file));      
                break;    
            default:      
                checksum = DigestUtils.md5Hex(new FileInputStream(file));    
        }
        return checksum;  
}

三、使用com.google.common.io.Files.hash



    使用Guava來完成,Maven工程需要新增依賴包,如:

<!--  -->
<dependency>    
    <groupId>com.google.guava</groupId>    
    <artifactId>guava</artifactId>    
    <version>23.0</version>
</dependency>

    簡單程式碼如下,透過呼叫com.google.common.io.Files的hash方法即可:

public static String genChecksum3(File file, CheckSumAlgoType checkSumAlgoType) throws IOException {    
        /**     * 使用Guava     */    
        String checksum = null;    
        switch (checkSumAlgoType) {    
            case MD5:      
                checksum = com.google.common.io.Files.hash(file, Hashing.md5()).toString();
                break;    
            case SHA_1:      
                checksum = com.google.common.io.Files.hash(file, Hashing.sha1()).toString();      
                break;
            case SHA_256:      
                checksum = com.google.common.io.Files.hash(file, Hashing.sha256()).toString();      
                break;    
            case SHA_512:      
                checksum = com.google.common.io.Files.hash(file, Hashing.sha512()).toString();      
                break;    
            default:      
                checksum = com.google.common.io.Files.hash(file, Hashing.md5()).toString();    
        }    
        return checksum;  
}


驗證

最後,我們一起來驗證一下上述幾種方法對檔案的checksum計算。

public static void main(String[] args) throws NoSuchAlgorithmException, IOException {    
File file = new File("/users/wmj/Downloads/poi-bin-4.1.0-20190412.tar.gz");    
        for (CheckSumAlgoType type : CheckSumAlgoType.values()) {      
            System.out.println("採用" + type.getName() + "計算checksum");      
            System.out.println(          String.format("method=%s,checksum=%s", "genChecksum1", genChecksum1(file, type.getName())));      
            System.out.println(String.format("method=%s,checksum=%s", "genChecksum2", genChecksum2(file, type)));      
            System.out.println(String.format("method=%s,checksum=%s", "genChecksum3", genChecksum3(file, type)));      
            System.out.println();    
        }  
}

執行結果如下:



採用MD5計算checksum
method=genChecksum1,checksum=2fa39c79790c29c53368ec0c14fdea97
method=genChecksum2,checksum=2fa39c79790c29c53368ec0c14fdea97
method=genChecksum3,checksum=2fa39c79790c29c53368ec0c14fdea97


採用SHA-256計算checksum
method=genChecksum1,checksum=d8db4f8228d87935ca46b0af72db68ad83f45b31d885e67b089d195b5ee800bb
method=genChecksum2,checksum=d8db4f8228d87935ca46b0af72db68ad83f45b31d885e67b089d195b5ee800bb
method=genChecksum3,checksum=d8db4f8228d87935ca46b0af72db68ad83f45b31d885e67b089d195b5ee800bb

採用SHA-512計算checksum
method=genChecksum1,checksum=87499ab94882605ee2f407fc66e24c613ae98896b8d5f527b6cd8c604574922fc72d148da42962b2ee30ad18cd712e3de42bfe14770261b07217717c52a738a9
method=genChecksum2,checksum=87499ab94882605ee2f407fc66e24c613ae98896b8d5f527b6cd8c604574922fc72d148da42962b2ee30ad18cd712e3de42bfe14770261b07217717c52a738a9
method=genChecksum3,checksum=87499ab94882605ee2f407fc66e24c613ae98896b8d5f527b6cd8c604574922fc72d148da42962b2ee30ad18cd712e3de42bfe14770261b07217717c52a738a9

採用SHA1計算checksum
method=genChecksum1,checksum=f56e42474fa81676d82a38ae6a8df67194a50b93
method=genChecksum2,checksum=f56e42474fa81676d82a38ae6a8df67194a50b93
method=genChecksum3,checksum=f56e42474fa81676d82a38ae6a8df67194a50b93


    我們可以看到,計算結果和Apache上顯示的checksum是一致的。


    本文主要給出了三種計算checksum的方式,包括:


  1. 使用java.security.MessageDigest

  2. 使用org.apache.commons.codec.digest.DigestUtils

  3. 使用com.google.common.io.Files.hash


當然,可能還有其它的實現方式和工具包,如果讀者發現其它的也可以同步一下,一起學習。




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1600/viewspace-2823522/,如需轉載,請註明出處,否則將追究法律責任。

相關文章