Michael: Java nach PHP

Hallo,

ich müsste eine Hash-Funktion aus einem Java-Code in PHP nachbauen, verstehe allerdings den Java-Code nicht richtig. Kann mir da vielleicht jemand auf die Sprünge helfen.

private String hash(String input) {
  MessageDigest sha;

try {
   sha = MessageDigest.getInstance("SHA");
   sha.update(input.getBytes());

byte[] md5Bytes = sha.digest(input.getBytes());

StringBuffer hexValue = new StringBuffer("*");

for (int i=0; i<md5Bytes.length; i++)
   {
       int val = md5Bytes[i] & 0xff;
       if (val < 16) hexValue.append("0");
       hexValue.append(Integer.toHexString(val));
   }

//Maintenance.debugOutput("Hashing "+input+" to "+hexValue.toString());
   return hexValue.toString();

} catch (NoSuchAlgorithmException e) {
   Maintenance.sendBug("Unable to find the SHA hash scheme!", e);
  }

return null;
 }

Kann mir jemand erklären, was da genau gemacht wird.
(Ich kann leider nur vermuten, dass der String erst in eine Abfolge von Bytes umgewandelt wird und dass die dann verschlüsselt wird, anstatt des Strings selbst. Warum und wie genau ist mir allerdings unklar.)

Vielen Dank und viele Grüße

  1. Hallo,

    private String hash(String input) {
      MessageDigest sha;

    try {
       sha = MessageDigest.getInstance("SHA");
       sha.update(input.getBytes());

    byte[] md5Bytes = sha.digest(input.getBytes());

    SHA auf den Eingabe-String anwenden. Das Ergebnis ist ein Byte-Array.

    StringBuffer hexValue = new StringBuffer("*");

    Der StringBuffer hexValue wird mit "*" vorbelegt. (Die erzeugten
    Hash-Werte fangen also immer mit einem Stern an.)

    for (int i=0; i<md5Bytes.length; i++)
       {
           int val = md5Bytes[i] & 0xff;

    Durch diesen "Hack" wird ein byte-Wert, der in der Logik eigentlich
    vorzeichenlos ist, durch den Wertebereich vom byte-Datentyp aber
    im Bereich von -128 bis +127 interpretiert wird, wieder in den eigentlich
    gewünschten vorzeichenlosen Wert "gewandelt" und in einen Integer
    gespeichert.

    Falls dich das genauer interessiert, kann ich darauf noch näher eingehen.

    if (val < 16) hexValue.append("0");

    Ist der Wert dann kleiner 16, wird zuerst noch eine "0" geschrieben,
    weil die nachfolgende Konvertierung der Zahl in einen HEX-Wert bei
    Zahlen <16 nur ein einstelliges Ergebnis zurückliefert.

    hexValue.append(Integer.toHexString(val));

    Hier wird dann der Wert in hexadezimale Schreibweise gewandelt und an
    den Ergebnis-StringBuffer angehängt.

    }

    //Maintenance.debugOutput("Hashing "+input+" to "+hexValue.toString());
       return hexValue.toString();

    Hier wird der StringBuffer in einen String gewandelt und zurückgeliefert.

    Im Grunde wird mit der Funktion also nichts anderes gemacht, als die
    einzelnen SHA-Werte (Bytes) jeweils separat in den Hex-Wert zu wandeln,
    an den Ergebnis-String anzuhängen und diesen zurückzuliefern.

    Gruß
    Slyh

  2. Hi,

    ich müsste eine Hash-Funktion aus einem Java-Code in PHP nachbauen, verstehe allerdings den Java-Code nicht richtig. Kann mir da vielleicht jemand auf die Sprünge helfen.

    Normalerweise mache ich das ja mit einem Bullenelektrifizierer, aber ausnahmsweise auch als Kommentar.

    Die ist eine Funktion, die eine kontinuierliche Bytefolge unbekannter Laenge frisst.

    private String hash(String input) {

    Eine Variable vom Type "MessageDigest"

    MessageDigest sha;

    Fehlerabfangen (ist ein ordentlicher Programmierer scheint's)

    try {

    Belegen der Variablen "sha" mit der Funktion ... ach, das brauchst Du fuer PHOP nicht. Hier wird zusammengfasst der Eingangsstring der SHA Funktion gefuettert und zwar zu Fuss. Das ist in PHP jedoch nicht noetig, da kannst Du das direkt uebergeben und bekommst auch direkt als Hexzahlen serviert, die hier erst im folgendem dazu gemacht werden.

    for (int i=0; i<md5Bytes.length; i++)
       {
           int val = md5Bytes[i] & 0xff;
           if (val < 16) hexValue.append("0");
           hexValue.append(Integer.toHexString(val));
       }

    Am Ende hast Du dann ein Sternchen vorne und den Hash dahinter.
    Wie gross ist eigentlich "md5bytes"?
    Vor allem erinnert mich die Sache mit dem Sternchen an ein Posting, das immer noch in der Forumshauptdatei steht, wenn ich mich nicht irre. Streng genommen ist das hier ein Doppelposting, gelle? ;-)
    Deshalb meine Frage:
    Hast Du die Debugmoeglichkeit hier mal genutzt?

    //Maintenance.debugOutput("Hashing "+input+" to "+hexValue.toString());

    Ist das nicht "doppelt gemoppelt"?

    return hexValue.toString();

    Fehlermeldung im Fehlerfall.

    } catch (NoSuchAlgorithmException e) {
       Maintenance.sendBug("Unable to find the SHA hash scheme!", e);
      }

    Fehler, wenn Fehlerauffang einen Fehler verursacht hat.

    return null;
    }

    (Ich kann leider nur vermuten, dass der String erst in eine Abfolge von Bytes umgewandelt wird

    Ein String _ist_ eine Abfolge von Bytes.

    und dass die dann verschlüsselt wird, anstatt des Strings selbst. Warum und wie genau ist mir allerdings unklar.)

    Koenntest Du eigentlich mal die eine oder andere Probe hierhinposten?
    Oder noch besser in Deinen Orginalthread?
    Ich habe da so meine Vermutungen, was genau schiefgelaufen sein koennte.

    so short

    Christoph Zurnieden

    1. Hallo Christoph,

      try {
      Fehlerabfangen (ist ein ordentlicher Programmierer scheint's)

      Das ist m.E. das Schöne an Java, der Compiler schmeisst Kompilierungsfehler
      sobald eine Checked Exception nicht abgefangen wird.
      Man kommt also gar nicht drum rum. ;)

      Grüße aus Berlin
      Mario

    2. Hallo,

      Ein String _ist_ eine Abfolge von Bytes.

      Nein, eine Abfolge von char.
      char sind (intern) 2 Bytes lang.

      Gruß
      Sl "SCNR" yh

      1. Hi,

        Ein String _ist_ eine Abfolge von Bytes.

        Nein, eine Abfolge von char.
        char sind (intern) 2 Bytes lang.

        Nein, das waere der Java-Datentyp "String". Aber auch nur, wenn char der Java-Datentyp "Char" ist.
        Hey, wer mich im Beckmessern schlagen will muss schon frueher aufstehen! ;-)

        so short

        Christoph Zurnieden

        1. Hallo,

          Ein String _ist_ eine Abfolge von Bytes.
          Nein, eine Abfolge von char.
          char sind (intern) 2 Bytes lang.

          @Slyh: Mit Christoph bitte nicht so ungenau! ;-))
          Der Datenyp char ist in Java ein 16-bit Unicode character. Im Speicher ist er 2 Octets lang. Dass der Java-Datentyp byte im Speicher 1 Octet belegt, ist zwar wahr, hat aber nicht direkt etwas mit einer Umrechnung zwischen char und byte zu tun. Dass ein byte ein Octet (8 bit) lang ist, ist eine häufig, eben auch in Java, anzutreffende Festlegung, aber keine Grundeinheit der Datenverarbeitung.

          Nein, das waere der Java-Datentyp "String".

          Ja, hier ging es doch um Java, oder?

          Aber auch nur, wenn char der Java-Datentyp "Char" ist.

          Wo wird der Datentyp "Char" für Java definiert? Ich kenne nur char und die Class Character.

          Hey, wer mich im Beckmessern schlagen will muss schon frueher aufstehen! ;-)

          *g*

          btw: Woraus besteht ein Unicode-String in anderen Programmiersprachen, die Du nutzt?

          viele Grüße

          Axel

          1. Hi,

            @Slyh: Mit Christoph bitte nicht so ungenau! ;-))

            Davon gibt's hier mehrere.
            Aber das aendert nix ;-)

            Der Datenyp char ist in Java ein 16-bit Unicode character. Im Speicher ist er 2 Octets lang. Dass der Java-Datentyp byte im Speicher 1 Octet belegt, ist zwar wahr, hat aber nicht direkt etwas mit einer Umrechnung zwischen char und byte zu tun. Dass ein byte ein Octet (8 bit) lang ist, ist eine häufig, eben auch in Java, anzutreffende Festlegung, aber keine Grundeinheit der Datenverarbeitung.

            Ja, schon besser ;-)

            Nein, das waere der Java-Datentyp "String".
            Ja, hier ging es doch um Java, oder?

            Und PHP.

            Aber auch nur, wenn char der Java-Datentyp "Char" ist.
            Wo wird der Datentyp "Char" für Java definiert?

            Hae?
            Oh, was peinlich! ;-)

            btw: Woraus besteht ein Unicode-String in anderen Programmiersprachen, die Du nutzt?

            Das ist verschieden. Mindestgroesse wird natuerlich eingehalten.
            Ich habe aber auch sehr selten Gelegenheit mich mit Unicode direkt rumzuschlagen, meist kann ich es mehr oder weniger elegant vermeiden.

            so short

            Christoph Zurnieden

            1. Hallo,

              @Slyh: Mit Christoph bitte nicht so ungenau! ;-))
              Davon gibt's hier mehrere.

              Aber nur wenige, die zu "nörgeln" "beckmessern" sagen ;-)).

              viele Grüße

              Axel

              ps: Nein, ich bin kein Wagnerianer ;-)).

              1. Hi,

                @Slyh: Mit Christoph bitte nicht so ungenau! ;-))
                Davon gibt's hier mehrere.
                Aber nur wenige, die zu "nörgeln" "beckmessern" sagen ;-)).

                Na, da ist schon ein kleiner Unterschied!

                so short

                Christoph *SCNR ;-)* Zurnieden

    3. Hallo,

      Normalerweise mache ich das ja mit einem Bullenelektrifizierer, aber ausnahmsweise auch als Kommentar.

      Zu gütig von Dir ;-)

      Die ist eine Funktion, die eine kontinuierliche Bytefolge unbekannter Laenge frisst.

      Belegen der Variablen "sha" mit der Funktion ... ach, das brauchst Du fuer PHOP nicht. Hier wird zusammengfasst der Eingangsstring der SHA Funktion gefuettert und zwar zu Fuss. Das ist in PHP jedoch nicht noetig, da kannst Du das direkt uebergeben und bekommst auch direkt als Hexzahlen serviert, die hier erst im folgendem dazu gemacht werden.

      for (int i=0; i<md5Bytes.length; i++)
         {
             int val = md5Bytes[i] & 0xff;
             if (val < 16) hexValue.append("0");
             hexValue.append(Integer.toHexString(val));
         }

      Das kann nicht sein, dass das in PHP überflüssig ist. Wenn ich mit PHP einen String in den SHA-Hash wandle, kommt nämlich was anderes raus, als wenn das diese Java-Methode macht.
      Wenn ich es jetzt richtig verstanden habe, arbeitet der SHA Algorithmus nämlich nicht mit den einzelnen char sondern mit deren byte-Pendant.

      Wie gross ist eigentlich "md5bytes"?

      Wenn ich jetzt alles (alle Hilfen und Infos aus allen Antworten -> hier ein kurzes aber großes "Zwischendanke" an alle) richtig Ordnen kann ist md5bytes.length die Länge des übergebenen Strings, nur beinhaltet das Array nicht die char sondern entsprechende bytes (-> siehe Debuginfo von Mario).

      Vor allem erinnert mich die Sache mit dem Sternchen an ein Posting, das immer noch in der Forumshauptdatei steht, wenn ich mich nicht irre. Streng genommen ist das hier ein Doppelposting, gelle? ;-)

      Ja. Gut aufgepasst! Nur war bei meinem "original Post" noch unklar, wie diese Java-Methode genau implementiert ist. Da hat mir zum Glück der Entwickler von dem Ding weitergeholfen und mir den Code gemailt, bzw. mir einen Tipp gegeben, wo ich das in dem kompletten Programm finden kann. Deshalb hier ein neues Posting, bei dem ich nun wenigstens wusste, wie und wo genau das Problem ist. (Im anderen hatte ich ja auch noch die Fehlinfo, dass die Verschlüsselung md5 sei.)

      Ein String _ist_ eine Abfolge von Bytes.

      Ja. Nur wird es wahrscheinlich einen Unterschied machen, ob der Algorithmus nun "1" oder das "byte-Pendant "49"" ???

      Koenntest Du eigentlich mal die eine oder andere Probe hierhinposten?

      Kommt noch, mach mich jetzt mal an ein paar Versuche was zu implementieren.

      Oder noch besser in Deinen Orginalthread?

      Ich denke hier wird es dann besser aufgehoben sein.

      Ich habe da so meine Vermutungen, was genau schiefgelaufen sein koennte.

      Das, was ich oben versucht habe, zu beschreiben ?
      Oder hättest Du eine ganz andere Vermutung?

      Vielen Dank und viele Grüße

      Michael

      1. Hi,

        Das kann nicht sein, dass das in PHP überflüssig ist. Wenn ich mit PHP einen String in den SHA-Hash wandle, kommt nämlich was anderes raus, als wenn das diese Java-Methode macht.

        Dann ist einer der beiden Funktionen kaput.
        Tip: die von PHP ist i.O.

        Wenn ich es jetzt richtig verstanden habe, arbeitet der SHA Algorithmus nämlich nicht mit den einzelnen char sondern mit deren byte-Pendant.

        Das weist Du nicht, da er Dir nicht vorliegt.
        Es ist aber anzunehmen, das die SHA Funktion mit LittleEndian, 2er-Komplement Integern arbeitet und die Bits in Stuecken zu CHAR_BIT (fuer gewoehnlich 8) Bits frisst.

        Wie gross ist eigentlich "md5bytes"?
        Wenn ich jetzt alles (alle Hilfen und Infos aus allen Antworten -> hier ein kurzes aber großes "Zwischendanke" an alle) richtig Ordnen kann ist md5bytes.length die Länge des übergebenen Strings, nur beinhaltet das Array nicht die char sondern entsprechende bytes (-> siehe Debuginfo von Mario).

        Nein, da ist der Digest drin, also der Hash.

        Ein String _ist_ eine Abfolge von Bytes.

        Ja. Nur wird es wahrscheinlich einen Unterschied machen, ob der Algorithmus nun "1" oder das "byte-Pendant "49"" ???

        Nein, das ist nicht das Problem. Aber das input.getBytes ist tatsaechlich eines.

        Koenntest Du eigentlich mal die eine oder andere Probe hierhinposten?

        Kommt noch, mach mich jetzt mal an ein paar Versuche was zu implementieren.

        Was bitte hinderte Dich hier ein paar Vergleichshashes zu posten?
        Input: "blabla ..."
        PHP:   deadbeef...
        Java: *badbeef0...

        Oder noch besser in Deinen Orginalthread?
        Ich denke hier wird es dann besser aufgehoben sein.

        Eigentlich nicht, aber wen Du das moechtest ...

        Ich habe da so meine Vermutungen, was genau schiefgelaufen sein koennte.

        Das, was ich oben versucht habe, zu beschreiben ?
        Oder hättest Du eine ganz andere Vermutung?

        Wie die Kollegen schon bestaetigten liegt's am Input. Du hast bei PHP einen anderen Input als bei Java. Ganz genau scheint es wohl zu sein, das Du PHP mit latin1(? aber zumindest keinen Unicode) fuetterst und Java bekommt Unicode.
        Um das zu verifizieren waeren eben die Vergleichshashes nett gewesen, um die ich bat. Immerhin haette es ja noch an Gottweisswas liegen koennen, da wir hier den gesamten Code nicht vorliegen haben.
        Der Bug liegt uebrigens im Javateil, da SHA oktetweise Fuetterung voraussetzt, falls nichts anderes angegeben ist und ich mich da nicht irre.

        so short

        Christoph Zurnieden

  3. Hallo Michael,

    OK, jetzt ist mir Slyh schon zuvor gekommen, aber so ausführlich hätte
    ich dir auch nicht antworten können.
    Nichts desto trotz hier noch mein Post:

    Du hast es eigentlich schon recht erkannt. Der übergebene String wird
    bitweise verschlüsselt und anschließend hexadezimale dem StringBuffer
    angefügt.

    Ich habe das mal in einen Debugger gepackt. Vielleicht helfen Dir ja
    die erzeugten Werte der Parameter ein wenig weiter:

    Variable          Wert
    -------------------|----------------
    input-Wert:         testeintrag1
    input.getBytes()    [116, 101, 115, 116, 101, 105, 110, 116, 114, 97, 103, 49]
    md5Bytes            [-4, 58, -36, -114, 32, -24, 53, -40, -104, 84, -71, -14, 81, 88, -88, 44, 43, 6, 52, -104]
    hexValue.toString()  *fc3adc8e20e835d89854b9f25158a82c2b063498

    Und hier mal ein paar allgemeine Informationen zu den in der Klasse
    angewandten Methoden:

    Object MessageDigest
    Ein Message Digest ist eine Funktion, die zu einer gegebenen Nachricht eine
    Prüfziffer berechnet.

    MessageDigest.update()
    http://java.sun.com/j2se/1.4.2/docs/api/java/security/MessageDigest.html#update(byte[])
    public void update(byte[] input)
    Updates the digest using the specified array of bytes.
    Parameters: input - the array of bytes.

    MessageDigest.digest()
    http://java.sun.com/j2se/1.4.2/docs/api/java/security/MessageDigest.html#update(byte[])
    public byte[] digest(byte[] input)
    Performs a final update on the digest using the specified array of bytes, then completes the
    digest computation. That is, this method first calls update(input), passing the input array
    to the __update__ method, __then__ calls digest().
    Parameters: input - the input to be updated before the digest is completed.
    Returns: the array of bytes for the resulting hash value.

    Integer.toHexString()
    public static String toHexString(int i)
    Returns a string representation of the integer argument as an unsigned integer in base 16.

    Um das ganze nun nach PHP zu konvertieren, muss man sich mal schlau machen
    was bzw. wie PHP in dieser Sache etwas zu Verfügung stellt.
    Über eine php.net-Suche nach SHA gelangt man u.a. zu folgender SHA-Funktionsreferenz.

    Berechnet den SHA1 Hash von str unter Verwendung des US Secure

    Hash Algorithmus 1  und gibt den Hashwert zurück. Der Hash ist
      ein 40 Zeichen langer Hexadezimalwert. Ist der optionale Parameter
      raw_output TRUE, wird der SHA1-Extrakt im Raw-Binary-Format mit
      einer Länge von 20 Zeichen zurückgegeben. <<

    usw. usf...
    Denke, das wirst Du schon rausbekommen..

    BTW: SHA-1 wurde bereits anfang des Jahres geknackt.
    http://www.heise.de/newsticker/meldung/56428

    Wäre schön wenn Du die Funktion dann hier posten würdest ;)

    Grüße aus Berlin
    Mario

    1. Hallo Mario,

      OK, jetzt ist mir Slyh schon zuvor gekommen, aber so ausführlich hätte
      ich dir auch nicht antworten können.
      Nichts desto trotz hier noch mein Post:

      vielen Dank für Deine Mühen. Deine Infos haben die anderen Antworten nochmal super ergänzt und mir sehr weitergeholfen.

      Du hast es eigentlich schon recht erkannt. Der übergebene String wird
      bitweise verschlüsselt und anschließend hexadezimale dem StringBuffer
      angefügt.

      Nur wie ganau das abläuft verstehe ich leider immer noch nicht.

      Ich habe das mal in einen Debugger gepackt. Vielleicht helfen Dir ja
      die erzeugten Werte der Parameter ein wenig weiter:

      Auf alle Fälle tun sie das!

      Variable          Wert
      -------------------|----------------
      input-Wert:         testeintrag1
      input.getBytes()    [116, 101, 115, 116, 101, 105, 110, 116, 114, 97, 103, 49]
      md5Bytes            [-4, 58, -36, -114, 32, -24, 53, -40, -104, 84, -71, -14, 81, 88, -88, 44, 43, 6, 52, -104]
      hexValue.toString()  *fc3adc8e20e835d89854b9f25158a82c2b063498

      Was ich nicht nachvollziehen kann ist, der SHA-Algorithmus nun auf die Abfolge der Bytes (im Bsp. hier 116, 101, 115, etc.) angewendet wird und mit was genau dann der Array byte[] md5Bytes gefüllt wird.
      Mir ist auch nicht ganz klar, wie diese Bytefolge (116, etc.) an die sha-funktion übergeben wird.

      Hier mal ein PHP Bsp. dazu:

      $bytetest = '1';
      $bytetest = ord($bytetest);
      echo 'zu 1 wird der byte-wert '.$bytetest.' zurückgeliefert.'; //-> also 49, wie auch oben im Debug

      Ab hier fangen dann leider schon die Probleme wieder an, denn wenn ich $bytetest='11' an ord() übergebe, ist das Ergebnis nicht 49, 49 sondern 49.
      Wenn ich nun '11' char für char in ein Array schreibe also zb:

      $arr = array($bytetest, $bytetest);

      komme ich dem was input.getBytes() macht scheinbar etwas näher.

      var_dump($arr); liefert dann:
      array(2) { [0]=>int(49) [1]=>int(49)}

      Und hier gehen dann die Fragen richtig los.

      Ist "int(49)" hier gleichbedeutend mit dem byte-wert von '1', also 49 aus dem Debug-Output oben?

      Wäre dann ein entsprechender Array auch gleich zu der byte-Folge wie sie getBytes() liefert?

      Und wie kann/muss ich das dann in PHP an die SHA-Funktion übergeben?

      • SHA($arr) geht z.B. schon mal nicht.

      Um das ganze nun nach PHP zu konvertieren, muss man sich mal schlau machen
      was bzw. wie PHP in dieser Sache etwas zu Verfügung stellt.
      Über eine php.net-Suche nach SHA gelangt man u.a. zu folgender SHA-Funktionsreferenz.

      Berechnet den SHA1 Hash von str unter Verwendung des US Secure
        Hash Algorithmus 1  und gibt den Hashwert zurück. Der Hash ist
        ein 40 Zeichen langer Hexadezimalwert. Ist der optionale Parameter
        raw_output TRUE, wird der SHA1-Extrakt im Raw-Binary-Format mit
        einer Länge von 20 Zeichen zurückgegeben. <<

      Über die Referenz von PHP.net bin ich auch schon gestolpert, nur hat das nicht wirklich weitergeholfen da in PHP SHA1('testeintrag1') was anderes erzeugt, als diese Java-Methode.

      usw. usf...
      Denke, das wirst Du schon rausbekommen..

      Die Hoffnung stirbt zuletzt.

      BTW: SHA-1 wurde bereits anfang des Jahres geknackt.
      http://www.heise.de/newsticker/meldung/56428

      Netter Hinweis, aber eine echte Gefährdung ist da ja noch nicht abzusehen.

      Wäre schön wenn Du die Funktion dann hier posten würdest ;)

      Wenn ich es irgendwann rausbekomme... ;-)

      Danke und viele Grüße

      Michael