Java String zu SHA1

158

Ich versuche, einen einfachen String-zu-SHA1-Konverter in Java zu erstellen, und das habe ich ...

public static String toSHA1(byte[] convertme) {
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    } 
    return new String(md.digest(convertme));
}

Wenn ich es bestanden toSHA1("password".getBytes())habe, [�a�ɹ??�%l�3~��.weiß ich, dass es wahrscheinlich eine einfache Codierungskorrektur wie UTF-8 ist, aber könnte mir jemand sagen, was ich tun soll, um das zu bekommen, was ich will, was ist 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? Oder mache ich das völlig falsch?

Brian
quelle
Der Algorithmus ist SHA1ohne Bindestrich. Ich weiß nicht, ob dies einen Unterschied macht.
Der Scrum Meister
Es ist eine gute Übung die Zeichenkodierung angeben , wenn Sie anrufen getBytes(), zum Beispiel GebrauchtoSHA1("password".getBytes("UTF-8"))
Qwerky
Mögliches Duplikat von Java berechnet einen sha1 eines Strings
Tulains Córdova

Antworten:

183

UPDATE
Sie können den Apache Commons Codec (Version 1.7+) verwenden, um diesen Job für Sie zu erledigen.

DigestUtils.sha1Hex (stringToConvertToSHexRepresentation)

Vielen Dank an @ Jon Onstott für diesen Vorschlag.


Alte Antwort
Konvertieren Sie Ihr Byte-Array in einen Hex-String. In Real's How To erfahren Sie, wie .

return byteArrayToHexString(md.digest(convertme))

und (kopiert von Real's How To)

public static String byteArrayToHexString(byte[] b) {
  String result = "";
  for (int i=0; i < b.length; i++) {
    result +=
          Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
  }
  return result;
}

Übrigens erhalten Sie mit Base64 möglicherweise eine kompaktere Darstellung. Apache Commons Codec API 1.4 hat dieses nette Dienstprogramm, um alle Schmerzen zu lindern. siehe hier

Nishant
quelle
4
base64 und sha1 sind sehr unterschiedlich - schlagen Sie sie nicht als Alternativen vor.
Ryan A.
13
@ RyanA.: Soweit ich weiß, schlägt er base64 als Alternative zur Hex-Codierung des SHA1-Hashs vor (nicht als Alternative zu SHA1 vollständig).
Helmbert
Ich habe es noch nicht ausprobiert, aber möchten Sie erklären, wie das funktioniert?
Jivay
11
Warum nicht eine Bibliothek wie verwenden, DigestUtils.sha1Hex("my string")anstatt das Rad neu zu erfinden (obwohl es interessant ist zu wissen, wie man von Hand in Hex konvertiert)?
Jon Onstott
3
Denn als diese Antwort geschrieben wurde, hatte DigestUtils (1.7 wurde im September 2012 veröffentlicht) diese Funktion nicht. Vielen Dank für den Hinweis. +1
Nishant
67

Dies ist meine Lösung, um einen String in sha1 zu konvertieren. Es funktioniert gut in meiner Android App:

private static String encryptPassword(String password)
{
    String sha1 = "";
    try
    {
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(password.getBytes("UTF-8"));
        sha1 = byteToHex(crypt.digest());
    }
    catch(NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch(UnsupportedEncodingException e)
    {
        e.printStackTrace();
    }
    return sha1;
}

private static String byteToHex(final byte[] hash)
{
    Formatter formatter = new Formatter();
    for (byte b : hash)
    {
        formatter.format("%02x", b);
    }
    String result = formatter.toString();
    formatter.close();
    return result;
}
petrnohejl
quelle
7
Möglicherweise möchten Sie angeben, dass dies java.util.Formatter ist und am Ende ein formatter.close () benötigt, um eine Warnung zu vermeiden.
Eric Chen
Soll nicht encryptPassword("test")und echo test|sha1sumim Linux-Terminal das gleiche Ergebnis ausgegeben werden? Sie tun es nicht.
Tulains Córdova
@ TulainsCórdova Bezüglich des Konsolenaufrufs: Wenn Sie verwenden echo test, wird die Ausgabe einschließlich eines Zeilenumbruchs an weitergeleitet sha1sum. Wenn Sie eine einfache Zeichenfolge ohne Zeilenumbruch hashen möchten, können Sie verwenden echo -n test | sha1sum. Der -nParameter echobewirkt, dass der Zeilenumbruch weggelassen wird.
MrSnrub
Weniger auf die Frage, aber allgemeiner: Ihre encryptPassword()Klänge werden zum Speichern von Authentifizierungsdaten verwendet. Beachten Sie, dass Ihre Codierung für Wörterbuchangriffe anfällig ist, da kein Seeding angewendet wird. Überprüfen Sie in Ihrer Sicherheitsumgebung, ob dies ein Problem für Ihre Anwendung ist!
EagleRainbow
54

Verwenden der Guava Hashing-Klasse :

Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
Jan Schaefer
quelle
1
Diese Antwort muss möglicherweise aktualisiert werden, da jetzt eine Warnung ausgegeben wird, dass das Hashing instabil ist.
Semir Deljić
32

SHA-1 (und alle anderen Hashing-Algorithmen) geben Binärdaten zurück. Das bedeutet, dass sie (in Java) a produzieren byte[]. Das byteArray ist nicht repräsentieren keine bestimmte Zeichen, welche Mittel Sie nicht einfach in eine verwandeln kann Stringwie Sie haben.

Wenn Sie eine benötigen String, müssen Sie diese so formatieren, byte[]dass sie als dargestellt werden kann String(andernfalls behalten Sie einfach die byte[]Umgebung bei).

Zwei gebräuchliche Arten, beliebige byte[]als druckbare Zeichen darzustellen, sind BASE64 oder einfache Hex-Strings (dh jeweils bytedurch zwei hexadezimale Ziffern). Es sieht so aus, als würden Sie versuchen, einen Hex-String zu erzeugen.

Es gibt noch eine weitere Gefahr: Wenn Sie die SHA-1 eines Java erhalten möchten, Stringmüssen Sie diese Stringin eine byte[]erste konvertieren (da die Eingabe von SHA-1 ebenfalls eine ist byte[]). Wenn Sie einfach so verwenden, myString.getBytes()wie Sie es gezeigt haben, wird die Standardcodierung der Plattform verwendet und hängt als solche von der Umgebung ab, in der Sie sie ausführen (z. B. können je nach Sprache / Gebietsschema Ihres Betriebssystems unterschiedliche Daten zurückgegeben werden).

Eine bessere Lösung besteht darin, die für die Konvertierung zu verwendende StringCodierung byte[]wie folgt anzugeben : myString.getBytes("UTF-8"). Die Wahl von UTF-8 (oder einer anderen Codierung, die jedes Unicode-Zeichen darstellen kann) ist hier die sicherste Wahl.

Joachim Sauer
quelle
27

Dies ist eine einfache Lösung, die beim Konvertieren einer Zeichenfolge in ein Hex-Format verwendet werden kann:

private static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {

    MessageDigest crypt = MessageDigest.getInstance("SHA-1");
    crypt.reset();
    crypt.update(password.getBytes("UTF-8"));

    return new BigInteger(1, crypt.digest()).toString(16);
}
Nikita Koksharov
quelle
Warnung: Die Hash-Generierung ist für Hashes, die mit '0' beginnen, falsch. Sie erhalten einen String mit 39 Zeichen.
Philn
@philn könntest du eine Lösung vorschlagen?
Nikita
1
Ich denke, wenn Sie eine große Ganzzahl aus einem Byte [] mit genügend führenden Nullen erstellen, gehen diese Nullen verloren. Daher ist die Hex-String-Darstellung "0" nicht vorhanden, was zu einem Hash mit 39 oder noch weniger Zeichen führt. Ich habe oben petrnohejls Lösung verwendet und es funktioniert gut ...
Philn
25

Verwenden Sie einfach die Apache Commons Codec-Bibliothek. Sie haben eine Utility-Klasse namens DigestUtils

Keine Notwendigkeit, auf Details einzugehen.

DaTroop
quelle
51
Ich bin anderer Meinung, auf die Details
einzugehen ist sozusagen der springende
12
Die Frage ist, ob Sie Zeit haben, sich mit Details zu befassen oder nicht. Der springende Punkt ist normalerweise, es pünktlich zu erledigen. Nicht jeder ist Student oder hat den Luxus, alle Details zu lernen.
DaTroop
DigestUtils gibt ein Byte-Array zurück, damit Sie die Zeichenfolgendarstellung erhalten, die Sie zum Ausführen über Hex.encodeHexString benötigen. Java: Es ist 2014 und wir haben immer noch keine einstufige Sha-Methode
am
5
Ein Schritt SHA-1 Methode : String result = DigestUtils.sha1Hex("An input string"); o)
Jon Onstott
18

Wie bereits erwähnt, verwenden Sie den Apache Commons Codec. Es wird auch von Spring-Leuten empfohlen (siehe DigestUtils in Spring doc). Z.B:

DigestUtils.sha1Hex(b);

Würde definitiv nicht die bestbewertete Antwort hier verwenden.

Kazuar
quelle
6

Es wird nicht richtig gedruckt, da Sie die Base64-Codierung verwenden müssen. Mit Java 8 können Sie mit der Base64- Encoder-Klasse codieren .

public static String toSHA1(byte[] convertme) {
    md = MessageDigest.getInstance("SHA-1");
    return Base64.getEncoder().encodeToString((md.digest(convertme));
}

Ergebnis

Dies gibt Ihnen Ihre erwartete Ausgabe von 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

Eduardo Dennis
quelle
1
@Devenv es ist SHA-1 die drei Punkte bedeuten, dass es seinen ursprünglichen Code beibehält, der in sha1 konvertiert wird. Das ursprüngliche Problem von OP war das korrekte Drucken der Zeichenfolge.
Eduardo Dennis
4

Message Digest (Hash) ist Byte [] in Byte [] out

Ein Message Digest ist als eine Funktion definiert, die ein Rohbyte-Array verwendet und ein Rohbyte-Array (auch bekannt als byte[]) zurückgibt . Zum Beispiel hat SHA-1 (Secure Hash Algorithm 1) eine Digest-Größe von 160 Bit oder 20 Byte. Raw-Byte-Arrays können normalerweise nicht als Zeichencodierungen wie UTF-8 interpretiert werden , da nicht jedes Byte in jeder Reihenfolge eine zulässige Codierung ist. Also konvertiere sie in ein Stringmit:

new String(md.digest(subject), StandardCharsets.UTF_8)

Möglicherweise werden einige unzulässige Sequenzen erstellt oder Codezeiger auf undefinierte Unicode- Zuordnungen:

[�a�ɹ??�%l3~��.

Binär-zu-Text-Codierung

Hierfür wird die Binär-Text- Codierung verwendet. Bei Hashes wird am häufigsten die HEX-Codierung oder Base16 verwendet . Grundsätzlich kann ein Byte den Wert von 0bis haben255 (oder -128bis 127vorzeichenbehaftet) haben, der der HEX-Darstellung von 0x00- entspricht 0xFF. Daher verdoppelt hex die erforderliche Länge der Ausgabe, dh eine 20-Byte-Ausgabe erzeugt eine 40 Zeichen lange Hex-Zeichenfolge, z.

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Beachten Sie, dass keine Hex-Codierung erforderlich ist. Sie könnten auch so etwas wie base64 verwenden . Hex wird oft bevorzugt, weil es für Menschen leichter lesbar ist und eine definierte Ausgabelänge hat, ohne dass eine Polsterung erforderlich ist.

Sie können ein Byte-Array nur mit JDK-Funktionalität in Hex konvertieren:

new BigInteger(1, token).toString(16)

Beachten Sie jedoch, dass BigInteger das angegebene Byte-Array als Zahl und nicht als Byte-Zeichenfolge interpretiert wird . Das bedeutet, dass führende Nullen nicht ausgegeben werden und die resultierende Zeichenfolge kürzer als 40 Zeichen sein kann.

Verwenden von Bibliotheken zum Codieren in HEX

Sie können jetzt eine nicht getestete Byte-zu-Hex-Methode aus dem Stapelüberlauf kopieren und einfügen oder massive Abhängigkeiten wie Guava verwenden .

Um eine Lösung für die meisten bytebezogenen Probleme zu finden, habe ich ein Dienstprogramm implementiert, um diese Fälle zu behandeln: bytes-java (Github)

Um Ihr Nachrichten-Digest-Byte-Array zu konvertieren, können Sie dies einfach tun

String hex = Bytes.wrap(md.digest(subject)).encodeHex();

oder Sie können einfach die integrierte Hash-Funktion verwenden

String hex =  Bytes.from(subject).hashSha1().encodeHex();
Patrick Favre
quelle
2

Basis 64 Darstellung von SHA1Hash:

String hashedVal = Base64.getEncoder().encodeToString(DigestUtils.sha1(stringValue.getBytes(Charset.forName("UTF-8"))));
MOPS
quelle
1

Der Grund, warum dies nicht funktioniert, ist, dass Sie String(md.digest(convertme))Java beim Aufrufen anweisen, eine Folge verschlüsselter Bytes als Zeichenfolge zu interpretieren. Sie möchten die Bytes in hexadezimale Zeichen konvertieren.

Zarkonnen
quelle
0

Konvertieren Sie das Byte-Array in eine Hex-Zeichenfolge.

public static String toSHA1(byte[] convertme) {
    final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    byte[] buf = md.digest(convertme);
    char[] chars = new char[2 * buf.length];
    for (int i = 0; i < buf.length; ++i) {
        chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
        chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
    }
    return new String(chars);
}
abhihere
quelle