Unterschiedliche Ergebnisse mit Javas Digest im Vergleich zu externen Dienstprogrammen

194

Ich habe eine einfache Java-Klasse geschrieben, um die Hash-Werte der Windows Calculator-Datei zu generieren. Ich benutze Windows 7 Professional with SP1. Ich habe es versucht Java 6.0.29und Java 7.0.03. Kann mir jemand sagen, warum ich andere Hash-Werte von Java erhalte als (viele!) Externe Dienstprogramme und / oder Websites? Alles, was extern ist, stimmt überein, nur Java liefert unterschiedliche Ergebnisse.

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

}
Mike Viens
quelle
Ich denke dein toHex ist falsch. Wenn Sie dies int newElement = ((int) element) & 0xffstattdessen tun und verwenden, würde dies Ihr Problem lösen?
Zapl
64
Kopieren Sie die Datei parallel zur Berechnung der Prüfsumme in eine temporäre Datei, damit Sie vergleichen können, was Java mit dem erhält, was Sie erhalten, wenn Sie andere Tools verwenden. Windows kann so seltsam sein ... Ich habe nie gesehen, dass Java einen Fehler bei der Berechnung von Hashes gemacht hat ...
Pawel Veselov
3
Alle Programmierer sollten so programmieren! Der Code ist sehr sauber und ordentlich.
Martijn Courteaux
2
@ user567496: Für das, was es wert ist, gibt Ihr Code die richtigen SHA-1-Hashes im Vergleich zu anderen Java SHA-1-Implementierungen und im Vergleich zur Befehlszeile sha1sum util ... (getestet mit Dateien unter Linux, nicht mit calc.exe)
TacticalCoder
1
@Fido: In diesem Fall könnte es kein Zeichensatzproblem sein, da OP rohe Bytes liest: Er dekodiert keine Zeichen.
TacticalCoder

Antworten:

239

Verstanden. Das Windows-Dateisystem verhält sich je nach Architektur Ihres Prozesses unterschiedlich. Dieser Artikel erklärt alles - insbesondere:

Aber was ist mit 32-Bit-Anwendungen, bei denen der Systempfad fest codiert ist und die unter 64-Bit-Windows ausgeführt werden? Wie können sie den neuen SysWOW64-Ordner finden, ohne den Programmcode zu ändern? Die Antwort lautet, dass der Emulator Aufrufe des System32-Ordners transparent in den SysWOW64-Ordner umleitet. Selbst wenn der Ordner fest im System32-Ordner (wie C: \ Windows \ System32) codiert ist, stellt der Emulator sicher, dass stattdessen der SysWOW64-Ordner verwendet wird . Daher kann derselbe Quellcode, der den System32-Ordner verwendet, ohne Änderungen sowohl in 32-Bit- als auch in 64-Bit-Programmcode kompiliert werden.

Versuchen Sie, calc.exean einen anderen Ort zu kopieren. Führen Sie dann dieselben Tools erneut aus. Sie erhalten die gleichen Ergebnisse wie Java. Etwas am Windows-Dateisystem gibt den Tools andere Daten als Java ... Ich bin sicher, es hat etwas damit zu tun, dass es sich im Windows-Verzeichnis befindet und daher wahrscheinlich "anders" behandelt wird.

Außerdem habe ich es in C # ... reproduziert und festgestellt, dass es von der Architektur des von Ihnen ausgeführten Prozesses abhängt . Hier ist ein Beispielprogramm:

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

Und hier ist eine Konsolensitzung (minus Chatter vom Compiler):

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
Jon Skeet
quelle
64
Es gibt zwei Versionen von calc.exe: 64bit in C:\Windows\system32` and 32bit in C: \ Windows \ SysWOW64`. Zur Kompatibilität in einem 32-Bit-Prozess C:\Windows\system32` is mapped to C: \ Windows \ SysWOW64`. 64-Bit-Prozesse starten den 64-Bit-Calc, 32-Bit-Prozesse den 32-Bit-Calc. Kein Wunder, dass ihre Prüfsummen unterschiedlich sind. Wenn Sie die Datei geöffnet halten und mit handles.exeoder Process Explorer suchen, sehen Sie den anderen Pfad.
Richard
25
@ Jon Das ist als Dateisystem-Redirector bekannt.
David Heffernan
9
@ DavidHeffernan Die Meinungen variieren, möglicherweise zusammen mit der Definition von "lebensfähig". All diese Virtualisierung verstößt gegen das Prinzip der geringsten Überraschung und erhöht die Kosten (Zuweisung und Laufzeit). Andere Betriebssysteme bieten sowohl eine bessere 32-auf-64-Unterstützung als auch eine bessere Anwendungsvirtualisierung mit weniger Fehlern / undichten Abstraktionen (versuchen Sie, Garbage Collection-Programme auf Wow64 auszuführen, oder vergleichen Sie MD5-Summen wie das OP und einige andere Nischenfälle).
sehe
5
Manchmal frage ich mich, ob die Leute dich positiv bewerten, weil du Jon Skeet bist, nicht nur wegen der Antwort. Ich sage nicht, dass die Antwort nicht gut oder so ist, aber 145 positive Stimmen, wenn die Antwort "Etwas passiert in Windows" lautet (um fair zu sein, geben Sie einen Link an, aber immer noch), scheinen die Leute mehr zu überlegen als nur Ihre Antwort, wenn sie stimmen zu. Ich hasse dich nicht, aber das bedeutet nur, dass es eine Weile dauern wird, bis ich dich
Jason Ridge
5
Der Blog ist, wie ich es gefunden habe. Ich hatte auf etwas Jon Skeet Magie gehofft, aber ich hatte das Gefühl "Hey, das hätte ich tun können". Wahrscheinlich nicht annähernd so schnell, aber los geht's. Ok, vielleicht hätte ich es nicht können, aber trotzdem. Die Kappe ist wenig tröstlich, denn das bedeutet nur, dass Sie sie an einem bestimmten Tag erreichen werden, und ich kann Sie daher nie einholen. Na ja ...
Jason Ridge