Hasil yang berbeda dengan intisari Java versus utilitas eksternal

194

Saya telah menulis kelas Java sederhana untuk menghasilkan nilai hash dari file Windows Calculator. Saya menggunakan Windows 7 Professional with SP1. Saya sudah mencoba Java 6.0.29dan Java 7.0.03. Dapatkah seseorang memberi tahu saya mengapa saya mendapatkan nilai hash yang berbeda dari Java versus (banyak!) Utilitas dan / atau situs web eksternal? Semuanya eksternal cocok satu sama lain, hanya Java yang memberikan hasil berbeda.

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
sumber
Saya kira toHex Anda salah. Jika Anda melakukannya int newElement = ((int) element) & 0xffdan menggunakannya, apakah itu akan menyelesaikan masalah Anda?
zapl
64
Sejalan dengan menghitung jumlah cek, salin file ke beberapa file temp, sehingga Anda dapat membandingkan apa yang Java dapatkan dengan apa yang Anda dapatkan ketika Anda menggunakan alat lain. Windows mungkin aneh seperti itu ... Saya tidak pernah melihat Jawa membuat kesalahan dalam menghitung hash ...
Pawel Veselov
3
Semua programmer harus memprogram seperti ini! Kode ini sangat bersih dan rapi.
Martijn Courteaux
2
@ user567496: sesuai nilainya, kode Anda memberikan hash SHA-1 yang benar dibandingkan dengan implementasi Java SHA-1 lainnya dan dibandingkan dengan commandline sha1sum util ... (diuji dengan file di Linux, bukan dengan calc.exe)
TacticalCoder
1
@Fido: dalam hal ini tidak bisa menjadi masalah charset karena OP membaca byte mentah: dia bukan karakter decoding.
TacticalCoder

Jawaban:

239

Mengerti. Sistem file Windows berperilaku berbeda tergantung pada arsitektur proses Anda. Artikel ini menjelaskan semuanya - khususnya:

Tetapi bagaimana dengan aplikasi 32-bit yang memiliki jalur sistem hard code dan sedang berjalan di Windows 64-bit? Bagaimana mereka dapat menemukan folder SysWOW64 baru tanpa perubahan dalam kode program, Anda mungkin berpikir. Jawabannya adalah bahwa emulator mengalihkan panggilan ke folder System32 ke folder SysWOW64 secara transparan sehingga meskipun folder tersebut dikodekan ke folder System32 (seperti C: \ Windows \ System32), emulator akan memastikan bahwa folder SysWOW64 digunakan sebagai gantinya. . Jadi kode sumber yang sama, yang menggunakan folder System32, dapat dikompilasi untuk kode program 32-bit dan 64-bit tanpa perubahan.

Coba salin calc.exeke tempat lain ... kemudian jalankan alat yang sama lagi. Anda akan mendapatkan hasil yang sama dengan Java. Sesuatu tentang sistem file Windows memberikan data yang berbeda untuk alat-alat daripada memberikannya ke Java ... Saya yakin itu ada hubungannya dengan itu berada di direktori Windows, dan dengan demikian mungkin ditangani "berbeda".

Selanjutnya, saya telah mereproduksi dalam C # ... dan menemukan bahwa itu tergantung pada arsitektur proses yang Anda jalankan . Jadi inilah contoh programnya:

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

Dan inilah sesi konsol (minus obrolan dari kompiler):

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
sumber
64
Ada dua versi calc.exe: 64bit dalam C:\Windows\system32` and 32bit in C: \ Windows \ SysWOW64`. Untuk kompatibilitas dalam proses 32bit C:\Windows\system32` is mapped to C: \ Windows \ SysWOW64`. Proses 64bit akan meluncurkan kalk 64bit, proses 32bit kalk 32bit. Tidak mengejutkan checksum mereka berbeda. Jika Anda memegang file terbuka dan melihat dengan handles.exeatau Proses Explorer Anda akan melihat jalur yang berbeda.
Richard
25
@ Jon Bahwa ada sesuatu yang dikenal sebagai Pengarah Sistem File.
David Heffernan
9
@DavidHeffernan Opini bervariasi, mungkin bersama dengan definisi 'layak'. Semua virtualisasi ini melanggar prinsip paling tidak mengejutkan dan menambah biaya (alokasi dan runtime). Sistem operasi lain berhasil memberikan dukungan 32-on-64 yang lebih baik dan virtualisasi aplikasi yang lebih baik dengan lebih sedikit hambatan / abstraksi bocor (coba jalankan program pengumpulan sampah di Wow64, atau coba bandingkan jumlah md5 seperti OP, dan beberapa case niche lainnya).
lihat
5
Kadang-kadang saya bertanya-tanya apakah orang-orang membuat Anda jengkel karena Anda jeet skeet, bukan semata - mata karena jawabannya. Saya tidak mengatakan bahwa jawabannya tidak baik atau apa pun, tetapi 145 suara positif ketika jawabannya adalah "Sesuatu sedang terjadi di windows" (agar adil Anda memang menyediakan tautan, tapi tetap saja) sepertinya orang mempertimbangkan lebih dari sekadar jawaban Anda saat mereka menang. Saya tidak membenci Anda, tetapi ini hanya berarti akan lama sebelum saya menyusul Anda: P
Jason Ridge
5
Blog adalah bagaimana saya menemukannya. Saya berharap untuk beberapa sulap Jon Skeet tetapi saya merasa seperti "Hei, saya bisa melakukan itu". Mungkin tidak secepat tapi di sana Anda pergi. Ok mungkin aku tidak bisa, tapi tetap saja. Sedangkan untuk topi, ada sedikit penghiburan di dalamnya karena itu hanya berarti bahwa setiap hari Anda akan mencapainya, dan karena itu saya tidak akan pernah bisa mengejar Anda. Oh well ...
Jason Ridge