Résultats différents avec le résumé de Java par rapport aux utilitaires externes

194

J'ai écrit une classe Java simple pour générer les valeurs de hachage du fichier Windows Calculator. J'utilise Windows 7 Professional with SP1. J'ai essayé Java 6.0.29et Java 7.0.03. Quelqu'un peut-il me dire pourquoi j'obtiens des valeurs de hachage différentes de Java par rapport à (beaucoup!) D'utilitaires externes et / ou de sites Web? Tout ce qui est externe correspond, seul Java renvoie des résultats différents.

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
la source
Je suppose que votre toHex est faux. Si vous le faites int newElement = ((int) element) & 0xffet l'utilisez à la place, cela résoudrait-il votre problème?
2012
64
Parallèlement au calcul de la somme de contrôle, copiez le fichier dans un fichier temporaire, afin de pouvoir comparer ce que Java obtient avec ce que vous obtenez lorsque vous utilisez d'autres outils. Windows peut être bizarre comme ça ... Je n'ai jamais vu Java faire une erreur de calcul des hachages ...
Pawel Veselov
3
Tous les programmeurs devraient programmer comme ça! Le code est très propre et soigné.
Martijn Courteaux
2
@ user567496: pour ce qu'il vaut votre code donne les hachages SHA-1 corrects par rapport à d'autres implémentations Java SHA-1 et par rapport à la ligne de commande sha1sum util ... (testé avec des fichiers sur Linux, pas avec calc.exe)
TacticalCoder
1
@Fido: dans ce cas, cela ne peut pas être un problème de jeu de caractères car OP lit les octets bruts: il ne décode pas les caractères.
TacticalCoder

Réponses:

239

Je l'ai. Le système de fichiers Windows se comporte différemment selon l'architecture de votre processus. Cet article explique tout - en particulier:

Mais qu'en est-il des applications 32 bits dont le chemin système est codé en dur et s'exécutent dans un Windows 64 bits? Comment peuvent-ils trouver le nouveau dossier SysWOW64 sans changements dans le code du programme, vous pourriez penser. La réponse est que l'émulateur redirige les appels vers le dossier System32 vers le dossier SysWOW64 de manière transparente, donc même si le dossier est codé en dur vers le dossier System32 (comme C: \ Windows \ System32), l'émulateur s'assurera que le dossier SysWOW64 est utilisé à la place . Ainsi, le même code source, qui utilise le dossier System32, peut être compilé en code programme 32 bits et 64 bits sans aucune modification.

Essayez de copier calc.exeailleurs ... puis exécutez à nouveau les mêmes outils. Vous obtiendrez les mêmes résultats que Java. Quelque chose dans le système de fichiers Windows donne aux outils des données différentes de celles qu'ils donnent à Java ... Je suis sûr que cela a quelque chose à voir avec le fait qu'il soit dans le répertoire Windows, et donc probablement traité "différemment".

De plus, je l'ai reproduit en C # ... et j'ai découvert que cela dépend de l' architecture du processus que vous exécutez . Voici donc un exemple de programme:

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

Et voici une session de console (sans bavardage du compilateur):

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
la source
64
Il existe deux versions de calc.exe: 64 bits dans C:\Windows\system32` and 32bit in C: \ Windows \ SysWOW64`. Pour la compatibilité dans un processus 32 bits C:\Windows\system32` is mapped to C: \ Windows \ SysWOW64`. Les processus 64 bits lanceront le calcul 64 bits, les processus 32 bits le calcul 32 bits. Pas étonnant que leurs sommes de contrôle soient différentes. Si vous maintenez le fichier ouvert et regardez avec handles.exeou Process Explorer, vous verrez le chemin différent.
Richard
25
@ Jon Que quelque chose est connu comme le redirecteur de système de fichiers.
David Heffernan
9
@DavidHeffernan Les opinions varient, peut-être avec la définition de «viable». Toute cette virtualisation viole le principe de la moindre surprise et ajoute des coûts (allocation et runtime). D'autres systèmes d'exploitation parviennent à fournir à la fois une meilleure prise en charge 32 sur 64 et une meilleure virtualisation des applications avec moins d'accrochages / abstractions qui fuient (essayez d'exécuter des programmes de collecte de déchets sur Wow64, ou essayez de comparer les sommes md5 comme l'OP, et quelques autres cas de niche).
sehe
5
Parfois, je me demande si les gens vous votent positivement parce que vous êtes jon skeet, pas uniquement à cause de la réponse. Je ne dis pas que la réponse n'est pas bonne ou quoi que ce soit, mais 145 votes positifs lorsque la réponse est "Quelque chose se passe dans les fenêtres" (pour être honnête, vous fournissez un lien, mais quand même) semble que les gens envisagent plus que votre réponse quand ils votent. Je ne te déteste pas, mais cela signifie juste que ça va prendre un certain temps avant que je ne te rattrape: P
Jason Ridge
5
Le blog est comment je l'ai trouvé. J'espérais de la magie de Jon Skeet mais je me sentais comme "Hé, j'aurais pu faire ça". Probablement pas aussi rapidement, mais voilà. Ok peut-être que je n'aurais pas pu, mais quand même. Quant à la casquette, il y a peu de consolation car cela signifie simplement qu'un jour donné vous y arriverez, et je ne pourrai donc jamais vous rattraper. Eh bien ...
Jason Ridge