Resultados diferentes com o resumo do Java versus os utilitários externos

194

Eu escrevi uma classe Java simples para gerar os valores de hash do arquivo da Calculadora do Windows. estou usandoWindows 7 Professional with SP1 . Eu tentei Java 6.0.29e Java 7.0.03. Alguém pode me dizer por que estou obtendo valores de hash diferentes do Java versus (muitos!) Utilitários externos e / ou sites? Tudo externo corresponde um ao outro, apenas Java está retornando 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
fonte
Eu acho que o seu toHex está errado. Se você fizer int newElement = ((int) element) & 0xffe usar isso, isso resolveria o seu problema?
zapl
64
Paralelamente ao cálculo da soma de verificação, copie o arquivo para algum arquivo temporário, para poder comparar o que o Java obtém com o que obtém ao usar outras ferramentas. Windows pode ser estranho assim ... Eu nunca vi Java cometer um erro calcular hashes ...
Pawel Veselov
3
Todos os programadores devem programar assim! O código é muito limpo e arrumado.
Martijn Courteaux 15/03/12
2
@ user567496: pelo que vale a pena, seu código fornece os hashes SHA-1 corretos em comparação com outras implementações Java SHA-1 e em comparação com o utilitário sha1sum da linha de comando ... (testado com arquivos no Linux, não com calc.exe)
TacticalCoder
1
@Fido: neste caso, não poderia ser um problema de charset porque o OP está lendo bytes brutos: ele não está decodificando caracteres.
TacticalCoder

Respostas:

239

Entendi. O sistema de arquivos do Windows está se comportando de maneira diferente, dependendo da arquitetura do seu processo. Este artigo explica tudo - em particular:

Mas e os aplicativos de 32 bits que possuem o caminho do sistema codificado e estão em execução no Windows de 64 bits? Como eles podem encontrar a nova pasta SysWOW64 sem alterações no código do programa, você pode pensar. A resposta é que o emulador redireciona as chamadas para a pasta System32 para a pasta SysWOW64 de maneira transparente, mesmo que a pasta seja codificada para a pasta System32 (como C: \ Windows \ System32), o emulador garantirá que a pasta SysWOW64 seja usada. . Portanto, o mesmo código-fonte, que usa a pasta System32, pode ser compilado no código de programa de 32 e 64 bits sem nenhuma alteração.

Tente copiar calc.exepara outro lugar ... e execute as mesmas ferramentas novamente. Você obterá os mesmos resultados que Java. Algo no sistema de arquivos do Windows está fornecendo dados diferentes para as ferramentas do que no Java ... Tenho certeza de que isso está relacionado ao fato de ele estar no diretório do Windows e, portanto, provavelmente ser tratado de forma "diferente".

Além disso, eu o reproduzi em C # ... e descobri que depende da arquitetura do processo que você está executando . Então, aqui está um exemplo de programa:

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

E aqui está uma sessão de console (menos a conversa do 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
fonte
64
Existem duas versões calc.exe: 64 bits em C:\Windows\system32` and 32bit in C: \ Windows \ SysWOW64`. Para compatibilidade em um processo de 32 bits, C:\Windows\system32` is mapped to C: \ Windows \ SysWOW64`. Os processos de 64 bits lançam o calc de 64 bits, os processos de 32 bits o calc de 32 bits. Não é de surpreender que suas somas de verificação sejam diferentes. Se você mantiver o arquivo aberto e procurar com handles.exeou no Process Explorer, verá o caminho diferente.
Richard
25
@ Jon Esse algo é conhecido como Redirecionador de Sistema de Arquivos.
David Heffernan
9
As opiniões do @DavidHeffernan variam, talvez junto com a definição de 'viável'. Toda essa virtualização viola o princípio da menor surpresa e adiciona custos (alocação e tempo de execução). Outros sistemas operacionais conseguem oferecer melhor suporte 32-em-64 e melhor virtualização de aplicativos com menos obstáculos / abstrações com vazamentos (tente executar programas de coleta de lixo no Wow64 ou tente comparar somas md5 como o OP e alguns outros casos de nicho).
214126 Ver todas as
5
Às vezes, me pergunto se as pessoas votaram em você porque você é idiota, não apenas por causa da resposta. Não estou dizendo que a resposta não seja boa nem nada, mas é positivo quando a resposta é "Algo está acontecendo no Windows" (para ser justo, você fornece um link, mas ainda assim) parece que as pessoas estão considerando mais do que apenas a sua resposta quando eles votaram. Eu não estou odiando em você, mas isso apenas significa que vai ser um tempo antes de eu pegar até você: P
Jason Ridge
5
O blog é como eu o encontrei. Eu estava esperando por alguma mágica de Jon Skeet, mas eu senti como "Ei, eu poderia ter feito isso". Provavelmente não tão rápido, mas lá vai você. Ok, talvez eu não pudesse ter, mas ainda assim. Quanto ao limite, há pouco consolo nele, porque isso significa apenas que em qualquer dia você o alcançará e, portanto, nunca poderei alcançá-lo. Ah, bem ...
Jason Ridge