Різні результати з дайджестом Java на відміну від зовнішніх утиліт


194

Я написав простий клас Java для створення хеш-значень файлу калькулятора Windows. Я використовую Windows 7 Professional with SP1. Я спробував Java 6.0.29і Java 7.0.03. Хтось може сказати мені, чому я отримую різні хеш-значення від Java порівняно з (багатьма!) Зовнішніми утилітами та / або веб-сайтами? Все зовнішнє поєднання між собою, лише Java повертає різні результати.

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

}

Я думаю, що ваш toHex неправильний. Якщо ви зробите це int newElement = ((int) element) & 0xffі скористаєтесь цим, це вирішило б вашу проблему?
zapl

64
Паралельно з розрахунком чекової суми скопіюйте файл у якийсь тимчасовий файл, щоб ви могли порівняти те, що отримує Java з тим, що ви отримуєте, коли використовуєте інші інструменти. Windows може бути таким дивним ... Я ніколи не бачив, щоб Java робила помилку, обчислюючи хеші ...
Pawel Veselov

3
Усі програмісти повинні програмувати так! Код дуже чистий і акуратний.
Мартійн Курто

2
@ user567496: для чого варто ваш код дає правильні хеші SHA-1 порівняно з іншими реалізацією Java SHA-1 і порівняно з командним рядком sha1sum util ... (перевірено файлами на Linux, а не з calc.exe)
TacticalCoder

1
@Fido: у цьому випадку це не могло бути проблемою із діаграмою, оскільки ОП читає необроблені байти: він не розшифровує символи.
TacticalCoder

Відповіді:


239

Зрозумів. Файлова система Windows поводиться по-різному в залежності від архітектури вашого процесу. Ця стаття пояснює все це, зокрема:

А як щодо 32-розрядних додатків, у яких системний шлях жорстко закодований і працює у 64-бітовій Windows? Як вони можуть знайти нову папку SysWOW64 без змін програмного коду, ви можете подумати. Відповідь полягає в тому, що емулятор перенаправляє виклики в папку System32 до папки SysWOW64 прозоро, тому навіть якщо папка важко закодована у папку System32 (наприклад, C: \ Windows \ System32), емулятор переконається, що замість цього використовується папка SysWOW64 . Отже, той самий вихідний код, який використовує папку System32, може бути скомпільований як в 32-бітний, так і в 64-бітний програмний код без будь-яких змін.

Спробуйте скопіювати calc.exeкудись інше ... потім знову запустіть ті самі інструменти. Ви отримаєте ті ж результати, що і Java. Щось щодо файлової системи Windows надає іншим інструментам інші дані, ніж надає Java ... Я впевнений, що це щось пов'язане з тим, що він знаходиться в каталозі Windows, і, таким чином, ймовірно, обробляється "інакше".

Крім того, я відтворив його в C # ... і з’ясував, що це залежить від архітектури запущеного процесу . Ось ось зразок програми:

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

А ось консольний сеанс (мінус чат від компілятора):

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

64
У C: \ Windows \ SysWOW64 " є дві версії calc.exe: 64bit C:\Windows\system32` and 32bit in . Для сумісності в 32- C:\Windows\system32` is mapped to бітовому процесі C: \ Windows \ SysWOW64 ". 64-бітові процеси будуть запускати 64-бітовий calc, 32-бітний обробляє 32-бітний calc. Не дивно, що їх контрольні суми різні. Якщо ви тримаєте файл відкритим і шукаєте за допомогою " handles.exeПровідник процесів", ви побачите інший шлях.
Річард

25
@Jon Щось відоме як перенаправник файлової системи.
Девід Геффернан

9
@DavidHeffernan Думки різняться, можливо, поряд із визначенням "життєздатного". Вся ця віртуалізація порушує принцип найменшого здивування та додає витрат (розподілу та часу виконання). Іншим операційним системам вдається забезпечити як кращу підтримку 32-на-64, так і кращу віртуалізацію прикладних програм з меншою кількістю затяжок / абстракцій (спробуйте запустити програми збору сміття на Wow64, або спробуйте порівняти суми md5, як OP, та кілька інших нішевих випадків).
sehe

5
Іноді мені цікаво, чи люди висловлюють за те, що ви скептичні, не тільки через відповідь. Я не кажу, що відповідь не є доброю чи що-небудь, але 145 підказує, коли відповідь "Щось відбувається у Windows" (якщо справедливо, ви надаєте посилання, але все-таки), схоже, люди розглядають більше, ніж просто вашу відповідь, коли вони підтримують. Я не ненавиджу тебе, але це просто означає, що пройде деякий час, перш ніж я наздогнаю тебе: P
Jason Ridge

5
Блог - це те, як я його знайшов. Я сподівався на якусь магію Джона Скіта, але відчув, що "Ей, я міг би це зробити". Напевно, не так швидко, але ви їдете. Гаразд, я б не міг, але все ж. Що стосується шапки, то в ній мало втіхи, бо це просто означає, що в будь-який день ви її досягнете, і тому я ніколи не можу вас наздогнати. Ну добре ...
Джейсон Райдж
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.