Risultati diversi con il digest di Java rispetto alle utility esterne


194

Ho scritto una semplice classe Java per generare i valori hash del file Calcolatrice di Windows. Sto usando Windows 7 Professional with SP1. Ho provato Java 6.0.29e Java 7.0.03. Qualcuno può dirmi perché sto ottenendo valori di hash diversi da Java rispetto a (molti!) Programmi di utilità esterni e / o siti Web? Tutto ciò che corrisponde all'esterno, solo Java restituisce risultati diversi.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.CRC32;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Checksum 
{
    private static int size = 65536;
    private static File calc = new File("C:/Windows/system32/calc.exe");

    /*
        C:\Windows\System32\calc.exe (verified via several different utilities)
        ----------------------------
        CRC-32b = 8D8F5F8E
        MD5     = 60B7C0FEAD45F2066E5B805A91F4F0FC
        SHA-1   = 9018A7D6CDBE859A430E8794E73381F77C840BE0
        SHA-256 = 80C10EE5F21F92F89CBC293A59D2FD4C01C7958AACAD15642558DB700943FA22
        SHA-384 = 551186C804C17B4CCDA07FD5FE83A32B48B4D173DAC3262F16489029894FC008A501B50AB9B53158B429031B043043D2
        SHA-512 = 68B9F9C00FC64DF946684CE81A72A2624F0FC07E07C0C8B3DB2FAE8C9C0415BD1B4A03AD7FFA96985AF0CC5E0410F6C5E29A30200EFFF21AB4B01369A3C59B58


        Results from this class
        -----------------------
        CRC-32  = 967E5DDE
        MD5     = 10E4A1D2132CCB5C6759F038CDB6F3C9
        SHA-1   = 42D36EEB2140441B48287B7CD30B38105986D68F
        SHA-256 = C6A91CBA00BF87CDB064C49ADAAC82255CBEC6FDD48FD21F9B3B96ABF019916B    
    */    

    public static void main(String[] args)throws Exception {
        Map<String, String> hashes = getFileHash(calc);
        for (Map.Entry<String, String> entry : hashes.entrySet()) {
            System.out.println(String.format("%-7s = %s", entry.getKey(), entry.getValue()));
        }
    }

    private static Map<String, String> getFileHash(File file) throws NoSuchAlgorithmException, IOException {
        Map<String, String> results = new LinkedHashMap<String, String>();

        if (file != null && file.exists()) {
            CRC32 crc32 = new CRC32();
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");

            FileInputStream fis = new FileInputStream(file);
            byte data[] = new byte[size];
            int len = 0;
            while ((len = fis.read(data)) != -1) {
                crc32.update(data, 0, len);
                md5.update(data, 0, len);
                sha1.update(data, 0, len);
                sha256.update(data, 0, len);
            }
            fis.close();

            results.put("CRC-32", toHex(crc32.getValue()));
            results.put(md5.getAlgorithm(), toHex(md5.digest()));
            results.put(sha1.getAlgorithm(), toHex(sha1.digest()));
            results.put(sha256.getAlgorithm(), toHex(sha256.digest()));
        }
        return results;
    }

    private static String toHex(byte[] bytes) {
        String result = "";
        if (bytes != null) {
            StringBuilder sb = new StringBuilder(bytes.length * 2);
            for (byte element : bytes) {
                if ((element & 0xff) < 0x10) {
                    sb.append("0");
                }
                sb.append(Long.toString(element & 0xff, 16));
            }
            result = sb.toString().toUpperCase();
        }
        return result;
    }

    private static String toHex(long value) {
        return Long.toHexString(value).toUpperCase();
    }

}

Immagino che il tuo toHex sia sbagliato. Se lo fai int newElement = ((int) element) & 0xffe lo usi invece, risolveresti il ​​tuo problema?
zapl

64
Parallelamente al calcolo della somma di controllo, copia il file in un file temporaneo, in modo da poter confrontare ciò che Java ottiene con ciò che ottieni quando usi altri strumenti. Windows potrebbe essere strano in quel modo ... Non ho mai visto Java fare un errore nel calcolo degli hash ...
Pawel Veselov

3
Tutti i programmatori dovrebbero programmare in questo modo! Il codice è molto pulito e ordinato.
Martijn Courteaux,

2
@ user567496: per quanto vale il tuo codice fornisce gli hash SHA-1 corretti rispetto ad altre implementazioni Java SHA-1 e confrontati con la riga di comando sha1sum util ... (testato con file su Linux, non con calc.exe)
TacticalCoder

1
@Fido: in questo caso non potrebbe essere un problema di set di caratteri perché OP sta leggendo byte non elaborati: non sta decodificando i caratteri.
TacticalCoder

Risposte:


239

Fatto. Il file system di Windows si comporta in modo diverso a seconda dell'architettura del processo. Questo articolo spiega tutto - in particolare:

Ma per quanto riguarda le applicazioni a 32 bit che hanno il percorso di sistema hard coded ed è in esecuzione in un Windows a 64 bit? Come possono trovare la nuova cartella SysWOW64 senza modifiche al codice del programma, si potrebbe pensare. La risposta è che l'emulatore reindirizza le chiamate alla cartella System32 sulla cartella SysWOW64 in modo trasparente, quindi anche se la cartella è codificata nella cartella System32 (come C: \ Windows \ System32), l'emulatore si assicurerà che venga invece utilizzata la cartella SysWOW64 . Quindi lo stesso codice sorgente, che utilizza la cartella System32, può essere compilato in codice di programma a 32 e 64 bit senza alcuna modifica.

Prova a copiare calc.exeda qualche altra parte ... quindi esegui di nuovo gli stessi strumenti. Otterrai gli stessi risultati di Java. Qualcosa nel file system di Windows sta fornendo agli strumenti dati diversi rispetto a quelli forniti a Java ... Sono sicuro che abbia a che fare con il fatto di trovarsi nella directory di Windows, e quindi probabilmente gestito in modo "diverso".

Inoltre, l'ho riprodotto in C # ... e ho scoperto che dipende dall'architettura del processo che stai eseguendo . Quindi, ecco un programma di esempio:

using System;
using System.IO;
using System.Security.Cryptography;

class Test
{
    static void Main()
    {
        using (var md5 = MD5.Create())
        {
            string path = "c:/Windows/System32/Calc.exe";
            var bytes = md5.ComputeHash(File.ReadAllBytes(path));
            Console.WriteLine(BitConverter.ToString(bytes));
        }
    }
}

Ed ecco una sessione della console (meno le chiacchiere dal compilatore):

c:\users\jon\Test>csc /platform:x86 Test.cs    

c:\users\jon\Test>test
60-B7-C0-FE-AD-45-F2-06-6E-5B-80-5A-91-F4-F0-FC

c:\users\jon\Test>csc /platform:x64 Test.cs

c:\users\jon\Test>test
10-E4-A1-D2-13-2C-CB-5C-67-59-F0-38-CD-B6-F3-C9

64
Esistono due versioni di calc.exe: 64 bit in C:\Windows\system32` and 32bit in C: \ Windows \ SysWOW64`. Per compatibilità in un processo a 32 bit C:\Windows\system32` is mapped to C: \ Windows \ SysWOW64`. I processi a 64 bit lanceranno il calcolo a 64 bit, i processi a 32 bit il calcolo a 32 bit. Non sorprende che i loro checksum siano diversi. Se tieni il file aperto e guardi con handles.exeo Process Explorer vedrai il diverso percorso.
Richard,

25
@Jon Quel qualcosa è noto come Redirector del file system.
David Heffernan,

9
@DavidHeffernan Le opinioni variano, forse insieme alla definizione di "praticabile". Tutta questa virtualizzazione viola il principio della minima sorpresa e aggiunge costi (allocazione e tempo di esecuzione). Altri sistemi operativi riescono a fornire sia un migliore supporto 32 su 64 sia una migliore virtualizzazione delle applicazioni con meno ostacoli / astrazioni che perdono (prova a eseguire programmi di raccolta dei rifiuti su Wow64 o prova a confrontare somme md5 come l'OP e alcuni altri casi di nicchia).
Sehe

5
A volte mi chiedo se le persone ti votano perché sei jon skeet, non solo per la risposta. Non sto dicendo che la risposta non sia buona o niente, ma 145 voti positivi quando la risposta è "Sta succedendo qualcosa in Windows" (per essere onesti fornisci un link, ma comunque) sembra che le persone stiano considerando più della tua risposta quando hanno votato. Non ti odio, ma questo significa solo che ci vorrà un po 'prima che ti raggiunga: P
Jason Ridge

5
Il blog è come l'ho trovato. Speravo in un po 'di magia di Jon Skeet ma mi sentivo come "Ehi, avrei potuto farlo". Probabilmente non altrettanto velocemente ma ci sei. Ok forse non avrei potuto, ma comunque. Per quanto riguarda il cappuccio, c'è poca consolazione in esso perché ciò significa solo che in un determinato giorno lo raggiungerai, e quindi non posso mai raggiungerti. Oh bene ...
Jason Ridge,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.