Stringa Java su SHA1


158

Sto cercando di creare un semplice convertitore da String a SHA1 in Java e questo è quello che ho ...

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

Quando lo passo toSHA1("password".getBytes()), capisco [�a�ɹ??�%l�3~��.che probabilmente è una semplice correzione di codifica come UTF-8, ma qualcuno potrebbe dirmi cosa dovrei fare per ottenere quello che voglio che è 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? O sto sbagliando completamente?


L'algoritmo è SHA1senza il trattino, non so se questo farà la differenza.
The Scrum Meister

È buona norma specificare la codifica dei caratteri quando si chiama getBytes(), ad esempio utilizzaretoSHA1("password".getBytes("UTF-8"))
Qwerky

Risposte:


183

AGGIORNAMENTO
Puoi usare Apache Commons Codec (versione 1.7+) per fare questo lavoro per te.

DigestUtils.sha1Hex (stringToConvertToSHexRepresentation)

Grazie a @ Jon Onstott per questo suggerimento.


Vecchia risposta
Converti la tua matrice di byte in stringa esadecimale. Real's How To ti dice come .

return byteArrayToHexString(md.digest(convertme))

e (copiato da 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;
}

A proposito, potresti ottenere una rappresentazione più compatta usando Base64. Apache Commons Codec API 1.4 , ha questa bella utility per eliminare tutto il dolore. fare riferimento qui


4
base64 e sha1 sono molto diversi: non suggerirli come alternative.
Ryan A.

13
@RyanA .: A quanto ho capito, suggerisce base64 come alternativa alla codifica esadecimale dell'hash SHA1 (non in alternativa a SHA1 del tutto).
helmbert,

Non l'ho ancora provato, ma ti dispiacerebbe spiegare come funziona?
Jivay,

11
Perché non usare una libreria come DigestUtils.sha1Hex("my string")invece di reinventare la ruota (anche se è interessante sapere come convertire in esadecimale a mano)?
Jon Onstott,

3
Perché quando questa risposta è stata scritta DigestUtils (la versione 1.7 è stata rilasciata nel settembre 2012) non aveva questa funzione. Grazie per averlo segnalato. +1
Nishant,

67

Questa è la mia soluzione per convertire la stringa in sha1. Funziona bene nella mia app Android:

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

7
Potrebbe voler specificare che questo è java.util.Formatter e necessita di un formatter.close () alla fine per evitare avvertimenti.
Eric Chen,

Shoudl't encryptPassword("test")e echo test|sha1sumnel terminale Linux hanno prodotto lo stesso risultato? Non lo fanno.
Tulains Córdova,

@ TulainsCórdova Per quanto riguarda l'invocazione della console: se si utilizza echo test, verrà eseguito il piping dell'output che include un'interruzione di riga sha1sum. Se si desidera eseguire l'hashing di una stringa semplice senza l'interruzione di riga finale, è possibile utilizzare echo -n test | sha1sum. Il -nparametro rende echoomessa l'interruzione di riga.
MrSnrub,

Meno alla domanda, ma più in generale: encryptPassword()sembra che venga utilizzato per la memorizzazione dei dati di autenticazione. Si noti che la codifica è vulnerabile agli attacchi del dizionario, poiché non viene applicato alcun seeding. Controlla il tuo ambiente di sicurezza se questo è un problema per la tua applicazione!
EagleRainbow,

54

Utilizzando la classe Hashing Guava :

Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()

1
Questa risposta potrebbe richiedere un aggiornamento in quanto ora genererà un avviso che indica che l'hashing è instabile.
Semir Deljić

32

SHA-1 (e tutti gli altri algoritmi di hashing) restituiscono dati binari. Ciò significa che (in Java) producono a byte[]. Questo byteallineamento non non rappresentano tutti i caratteri specifici, il che significa che non si può semplicemente trasformarlo in un Stringcome hai fatto.

Se hai bisogno di un String, allora devi formattarlo byte[]in un modo che può essere rappresentato come String(altrimenti, tieni solo il byte[]giro).

Due modi comuni di rappresentare arbitrari byte[]come caratteri stampabili sono BASE64 o stringhe esadecimali semplici (ovvero rappresentando ciascuno di essibyte con due cifre esadecimali). Sembra che tu stia provando a produrre una stringa esadecimale.

C'è anche un'altra trappola: se vuoi ottenere lo SHA-1 di un Java String, allora devi convertirlo Stringin un byte[]primo (poiché anche l'input di SHA-1 è un byte[]). Se si utilizza semplicemente myString.getBytes()come mostrato, utilizzerà la codifica predefinita della piattaforma e come tale dipenderà dall'ambiente in cui lo si esegue (ad esempio potrebbe restituire dati diversi in base all'impostazione della lingua / locale del proprio sistema operativo).

Una soluzione migliore è quella di specificare la codifica da utilizzare per la String-a-- byte[]conversione in questo modo: myString.getBytes("UTF-8"). Scegliere UTF-8 (o un'altra codifica che può rappresentare ogni carattere Unicode) è la scelta più sicura qui.


27

Questa è una soluzione semplice che può essere utilizzata durante la conversione di una stringa in un formato esadecimale:

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

Avvertenza: la generazione di hash non è corretta per gli hash che iniziano con '0'. Riceverai una stringa con 39 caratteri.
phil

@philn potresti suggerire una soluzione?
Nikita Koksharov,

1
Immagino che se si crea un intero grande da un byte [] con abbastanza zeri iniziali, questi 0 vengono persi. Pertanto, la rappresentazione della stringa esadecimale "0" non sarà presente, portando a un hash con 39 o anche meno caratteri. Ho usato la soluzione petrnohejls sopra e funziona benissimo ...
phil

25

Basta usare la libreria di codec di Apache Commons. Hanno una classe di utilità chiamata DigestUtils

Non c'è bisogno di entrare nei dettagli.


51
Non sono d'accordo, entrare nei dettagli è una specie di punto
ninesided

12
La domanda è se hai tempo per entrare nei dettagli o no. L'intero punto è di solito farlo in tempo. Non tutti sono studenti o hanno il lusso di apprendere tutti i dettagli.
DaTroop,

DigestUtils restituisce un array di byte, quindi per ottenere la rappresentazione di stringa è necessario eseguirlo tramite Hex.encodeHexString. Java: è il 2014 e non abbiamo ancora un metodo sha one step
Ryber

5
Metodo SHA-1 a un passo String result = DigestUtils.sha1Hex("An input string")
:;

18

Come menzionato prima di usare il codec commons apache. È consigliato anche dai ragazzi di Spring (vedi DigestUtils nel documento di Spring). Per esempio:

DigestUtils.sha1Hex(b);

Sicuramente non userei la risposta più votata qui.


7

Non viene stampata correttamente perché è necessario utilizzare la codifica Base64. Con Java 8 è possibile codificare utilizzando la classe di codificatore Base64 .

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

Risultato

Questo ti darà il risultato atteso di 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8


1
@Devenv è SHA-1 i tre punti significa che manterrà il suo codice originale che si converte in sha1. Il problema originale di OP era quando si stampava correttamente la stringa.
Eduardo Dennis,

4

Message Digest (hash) è byte [] in byte [] in uscita

Un digest di messaggio è definito come una funzione che accetta un array di byte non elaborato e restituisce un array di byte non elaborato (aka byte[]). Ad esempio SHA-1 (Secure Hash Algorithm 1) ha una dimensione digest di 160 bit o 20 byte. Gli array di byte non elaborati di solito non possono essere interpretati come codifiche di caratteri come UTF-8 , poiché non tutti i byte di ogni ordine sono legali per la codifica. Quindi convertendoli in a Stringcon:

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

potrebbe creare alcune sequenze illegali o avere puntatori di codice su mapping Unicode non definiti :

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

Codifica da binario a testo

Per questo viene utilizzata la codifica da binario a testo . Con gli hash, quello più utilizzato è la codifica HEX o Base16 . Fondamentalmente un byte può avere il valore 0di 255(o -128per 127firma) che è equivalente alla rappresentazione HEX 0x00- 0xFF. Quindi hex raddoppierà la lunghezza richiesta dell'output, il che significa che un output di 20 byte creerà una stringa esadecimale lunga 40 caratteri, ad esempio:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Si noti che non è necessario utilizzare la codifica esadecimale. Puoi anche usare qualcosa come base64 . L'esagono è spesso preferito perché è più facile da leggere dall'uomo e ha una lunghezza di uscita definita senza la necessità di imbottitura.

È possibile convertire un array di byte in esadecimale con la sola funzionalità JDK:

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

Si noti tuttavia che BigIntegerinterpreterà il dato array di byte come numero e non come stringa di byte. Ciò significa che gli zeri iniziali non verranno emessi e la stringa risultante potrebbe essere inferiore a 40 caratteri.

Utilizzo delle librerie per la codifica in HEX

Ora puoi copiare e incollare un metodo byte-to-hex non testato da Stack Overflow o utilizzare dipendenze enormi come Guava .

Per avere una soluzione di riferimento per la maggior parte dei problemi relativi ai byte ho implementato un'utilità per gestire questi casi: bytes-java (Github)

Per convertire il tuo array di byte digest dei messaggi potresti semplicemente fare

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

oppure potresti semplicemente usare la funzione hash integrata

String hex =  Bytes.from(subject).hashSha1().encodeHex();

2

Base 64 Rappresentazione di SHA1hash:

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

1

Il motivo per cui non funziona è che quando chiami String(md.digest(convertme)), stai dicendo a Java di interpretare una sequenza di byte crittografati come una stringa. Quello che vuoi è convertire i byte in caratteri esadecimali.


0

Converti array di byte in stringa esadecimale.

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.