Hashwert einer JPG Datei

Hallo an alle,

googlen und einige Stunden probieren haben mich nicht weiter gebracht. Ich suche eine Möglichkeit, mit Javascript den SHA-1 Hash von JPG Dateien zu berechnen. Hashfunktionen gibt es einige, das ist nicht das Problem. Allerdings hashen die alle Textstrings. Wenn ich mit Ajax den Content einer JPG Datei hole bekomme ich die binary Data auch als String, aber irgendwie scheint dabei was schief zu gehen. Im Ergebnis bekomme ich zwar immer einen Hash, aber der ist falsch. Ich habe schon alle mir bekannten Datenübertragungsoptionen ausprobiert, aber nichts hat funktioniert.

Hat jemand eine Idee, wie ich das Problem lösen kann?

Also wenn gar nichts geht, würde ich sagen mit und toDataURL.
Dann musst du aber auf die Cross Domain Policy achten.

Selbst das hab ich probiert. Base64 codiert kommt wieder ein anderer Hash heraus, aber immer noch nicht der richtige. Bei Canvas.toBlob der gleiche Effekt.

Folgende beiden Methoden hab ich probiert, in allen möglichen Varianten und mit unterschiedlichen Hashfunktionen

    var xhr1 = new XMLHttpRequest();
    xhr1.onreadystatechange = function(){
        if(xhr1.readyState == 4){
            console.log(CryptoJS.SHA1(xhr1.responseText));
        }
    };
    xhr1.open("GET", "blubb/IMG_blubb.jpg", true);
    xhr1.send();
};
function convertImgToBase64(url, callback, outputFormat){
    var canvas = document.createElement('CANVAS'),
        ctx = canvas.getContext('2d'),
        img = new Image();
    img.crossOrigin = 'Anonymous';
    img.onload = function(){
        var dataURL;
        canvas.height = img.height;
        canvas.width = img.width;
        ctx.drawImage(img, 0, 0);
        dataURL = canvas.toDataURL(outputFormat);
        callback.call(this, dataURL);
        canvas = null;
    };
    img.src = url;
}

Ich vermute ahnungslos, dass die erste Variante grundsätzlich richtig ist, aber irgendwie nicht die originalen Binarydaten ankommen, sondern durch irgendwelchen Header- oder Kodierungskram die Daten verfälscht werden. Leider bin ich da nicht wirklich kompetent.

Klappt doch?
http://jsfiddle.net/ugq2e718/1/

Oder irre ich mich?

Nein, leider nicht. Der Wert ist nicht „Richtig“. Wenn du die logo.png z.B. in einem online-Hashgenerator hashst (z.B. http://onlinemd5.com/ ) erhälst du den korrekten Hash von „22D85FA39825D4171B372635E9A7A0A7ACA87803“
Das Script errechnet „7fce54ab674b23936f82e8a80889b5564434f748“.

Zur Erklärung, warum mir das so wichtig ist:
Ich forsche immer noch an meinem Lichtfeldkamera Projekt. Mittlerweile bin ich so weit, dass ich aus den Daten JPGs für verschiedene Fokustiefen generieren kann. Für die Zuordnung der Bilder zu ihrer jeweiligen Focustiefe habe ich aber nur einen Jsonstring, der den Hashwert der JPG Datei enthält. Ich brauche daher den korrekten Hashwert der Dateien, den man erhält, wenn man das File serverseitig mit c oder php hasht.

Na gut.
Hier noch ein Link, vl hilft es ja:
https://code.google.com/p/crypto-js/issues/detail?id=67

Da hab ich nur die Hälfte verstanden. Das was ich verstanden habe deutet für mich darauf hin, dass es tatsächlich am Daten/Übertragungsformat liegt. Aber was ich tun muss um tatsächlich die realen Rohdaten der Datei in die Hashfunktion zu bekommen erschließt sich mir leider trotzdem nicht.

Ich denke das passt jetzt:
http://jsfiddle.net/ugq2e718/3/

Ahhh, sehr cool. Wenn ich das lese, versteh ich auch sofort warum es jetzt funktioniert. Dass es genauso ankommen muss, war mir klar, aber wie ich das bewirke, dazu hab ich nichts gefunden. Ich danke dir. Wie bist du dahinter gestiegen?

Für alle die beim Googlen mal auf diese Frage stoßen, falls das Fiddle nicht ewig lebt:

xhr.responseType = "arraybuffer"; 
xhr.onload = function( event ) {
    var typedArray = new Uint8Array( this.response );
    var sha1 = CryptoJS.SHA1(CryptoJS.lib.WordArray.create(typedArray)).toString().toUpperCase();
};

Kurz gegooglet wie man eine externe Datei in ein Typed Array lädt und anschließend die im issue beschriebene Lösung ergänzt. Also größtenteils Google-Arbeit :slight_smile: