Diferentes resultados con el resumen de Java versus las utilidades externas

194

He escrito una clase Java simple para generar los valores hash del archivo de la Calculadora de Windows. estoy usandoWindows 7 Professional with SP1 . Lo he intentado Java 6.0.29y Java 7.0.03. ¿Alguien puede decirme por qué obtengo diferentes valores hash de Java frente a (y muchos) utilidades externas y / o sitios web? Todo lo externo coincide entre sí, solo Java está devolviendo resultados diferentes.

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
fuente
Supongo que tu toHex está mal. Si lo hace int newElement = ((int) element) & 0xffy lo usa, ¿eso resolvería su problema?
zapl
64
Paralelamente al cálculo de la suma de verificación, copie el archivo en algún archivo temporal, para que pueda comparar lo que obtiene Java con lo que obtiene cuando usa otras herramientas. Windows puede ser así de extraño ... Nunca vi a Java cometer un error al calcular hashes ...
Pawel Veselov
3
¡Todos los programadores deberían programar así! El código es muy limpio y ordenado.
Martijn Courteaux
2
@ user567496: por lo que vale, su código proporciona los hash SHA-1 correctos en comparación con otras implementaciones Java SHA-1 y en comparación con la línea de comandos sha1sum util ... (probado con archivos en Linux, no con calc.exe)
TacticalCoder
1
@Fido: en este caso no podría ser un problema de juego de caracteres porque OP está leyendo bytes sin procesar: no está decodificando caracteres.
TacticalCoder

Respuestas:

239

Entendido. El sistema de archivos de Windows se comporta de manera diferente según la arquitectura de su proceso. Este artículo lo explica todo , en particular:

Pero, ¿qué pasa con las aplicaciones de 32 bits que tienen la ruta del sistema codificada y se ejecuta en un Windows de 64 bits? Puede pensar que pueden encontrar la nueva carpeta SysWOW64 sin cambios en el código del programa. La respuesta es que el emulador redirige las llamadas a la carpeta System32 a la carpeta SysWOW64 de forma transparente, por lo que incluso si la carpeta está codificada en la carpeta System32 (como C: \ Windows \ System32), el emulador se asegurará de que se use la carpeta SysWOW64. . Por lo tanto, el mismo código fuente, que usa la carpeta System32, se puede compilar en código de programa de 32 bits y de 64 bits sin ningún cambio.

Intente copiar calc.exeen otro lugar ... luego ejecute las mismas herramientas nuevamente. Obtendrá los mismos resultados que Java. Algo sobre el sistema de archivos de Windows está dando datos diferentes a las herramientas de lo que está dando a Java ... Estoy seguro de que tiene algo que ver con que esté en el directorio de Windows y, por lo tanto, probablemente se maneje "de manera diferente".

Además, lo he reproducido en C # ... y descubrí que depende de la arquitectura del proceso que esté ejecutando . Así que aquí hay un programa de muestra:

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

Y aquí hay una sesión de consola (menos charla del compilador):

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
fuente
64
Hay dos versiones de calc.exe: 64 bits en C:\Windows\system32` and 32bit in C: \ Windows \ SysWOW64`. Para compatibilidad en un proceso de 32 bits C:\Windows\system32` is mapped to C: \ Windows \ SysWOW64`. Los procesos de 64 bits lanzarán el cálculo de 64 bits, 32 bits procesará el cálculo de 32 bits. No es sorprendente que sus sumas de verificación sean diferentes. Si mantiene abierto el archivo y mira con handles.exeProcess Explorer, verá la ruta diferente.
Richard
25
@ Jon Ese algo se conoce como Redirector del sistema de archivos.
David Heffernan
9
@DavidHeffernan Las opiniones varían, quizás junto con la definición de 'viable'. Toda esta virtualización viola el principio de menor sorpresa y agrega costos (asignación y tiempo de ejecución). Otros sistemas operativos logran proporcionar un mejor soporte de 32 en 64 y una mejor virtualización de aplicaciones con menos inconvenientes / abstracciones con fugas (intente ejecutar programas de recolección de basura en Wow64, o intente comparar sumas md5 como el OP, y algunos otros casos de nicho).
sehe
55
A veces me pregunto si la gente te votó porque eres jon skeet, no solo por la respuesta. No estoy diciendo que la respuesta no sea buena ni nada, pero 145 votos positivos cuando la respuesta es "Algo está sucediendo en Windows" (para ser justos, usted proporciona un enlace, pero aún así) parece que la gente está considerando más que solo su respuesta cuando ellos votaron No te estoy odiando, pero esto solo significa que pasará un tiempo antes de que te alcance: P
Jason Ridge
55
El blog es como lo encontré. Esperaba algo de magia de Jon Skeet, pero sentí que "Hey, podría haber hecho eso". Probablemente no tan rápido, pero ahí lo tienes. Ok, tal vez no podría haberlo hecho, pero aún así. En cuanto a la tapa, hay poco consuelo porque eso solo significa que cualquier día lo alcanzarás y, por lo tanto, nunca puedo alcanzarte. Oh bien ...
Jason Ridge