String Java ke SHA1

158

Saya mencoba membuat konverter String to SHA1 sederhana di Java dan ini yang saya dapatkan ...

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

Ketika saya lulus toSHA1("password".getBytes()), saya [�a�ɹ??�%l�3~��.tahu saya mungkin memperbaiki encoding sederhana seperti UTF-8, tetapi bisakah seseorang memberi tahu saya apa yang harus saya lakukan untuk mendapatkan apa yang saya inginkan 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? Atau apakah saya melakukan ini sepenuhnya salah?

Brian
sumber
Algoritma SHA1tanpa tanda hubung, tidak tahu apakah itu akan membuat perbedaan.
The Scrum Meister
Ini praktik yang baik untuk menentukan pengkodean karakter saat Anda menelepon getBytes(), misalnya gunakantoSHA1("password".getBytes("UTF-8"))
Qwerky
kemungkinan duplikat Jawa menghitung sha1 dari String
Tulains Córdova

Jawaban:

183

PEMBARUAN
Anda dapat menggunakan Apache Commons Codec (versi 1.7+) untuk melakukan pekerjaan ini untuk Anda.

DigestUtils.sha1Hex (stringToConvertToSHexRepresentation)

Terima kasih kepada @ Jon Onstott untuk saran ini.


Jawaban Lama
Konversi Byte Array Anda ke Hex String. Cara Nyata memberitahu Anda caranya .

return byteArrayToHexString(md.digest(convertme))

dan (disalin dari 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;
}

BTW, Anda bisa mendapatkan representasi yang lebih kompak menggunakan Base64. Apache Commons Codec API 1.4 , memiliki utilitas yang bagus untuk menghilangkan semua rasa sakit. lihat disini

Nishant
sumber
4
base64 dan sha1 sangat berbeda - jangan menyarankan mereka sebagai alternatif.
Ryan A.
13
@RyanA .: Seperti yang saya pahami, ia menyarankan base64 sebagai alternatif untuk hex-encoding hash SHA1 (bukan sebagai alternatif untuk SHA1 sepenuhnya).
helmbert
Saya belum mencobanya, tetapi apakah Anda mau menjelaskan cara kerjanya?
Jivay
11
Mengapa tidak menggunakan perpustakaan seperti DigestUtils.sha1Hex("my string")alih - alih menciptakan kembali roda (meskipun menarik mengetahui cara mengonversi ke hex dengan tangan)?
Jon Onstott
3
Karena ketika jawaban ini ditulis DigestUtils (1,7 dirilis pada Sep 2012) tidak memiliki fitur itu. Terima kasih telah menunjukkan ini. +1
Nishant
67

Ini adalah solusi saya untuk mengubah string menjadi sha1. Ini bekerja dengan baik di aplikasi Android saya:

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
sumber
7
Mungkin ingin menentukan bahwa ini adalah java.util.Formatter dan membutuhkan formatter.close () di akhir untuk menghindari peringatan.
Eric Chen
Bukankah encryptPassword("test")dan echo test|sha1sumdi terminal linux menghasilkan hasil yang sama? Mereka tidak melakukannya.
Tulains Córdova
@ TulainsCórdova Mengenai permintaan konsol: Jika Anda menggunakan echo test, output termasuk jeda baris akan disalurkan ke sha1sum. Jika Anda ingin meng-hash string biasa tanpa mengekstrak line break, Anda bisa menggunakannya echo -n test | sha1sum. The -nparameter membuat echomenghilangkan garis istirahat.
MrSnrub
Lebih sedikit pertanyaan, tetapi lebih umum: encryptPassword()Suara Anda seperti itu digunakan untuk menyimpan data otentikasi. Perhatikan bahwa pengkodean Anda rentan terhadap serangan kamus, karena tidak ada seeding yang diterapkan. Periksa lingkungan keamanan Anda apakah ini merupakan masalah untuk aplikasi Anda!
EagleRainbow
54

Menggunakan kelas Jambu Hashing :

Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
Jan Schaefer
sumber
1
Jawaban ini mungkin memerlukan pembaruan karena sekarang ini akan menghasilkan peringatan bahwa Hashing tidak stabil.
Semir Delji
32

SHA-1 (dan semua algoritma hashing lainnya) mengembalikan data biner. Itu berarti (di Jawa) mereka menghasilkan a byte[]. Itu bytearray yang tidak tidak mewakili karakter tertentu, yang berarti Anda tidak bisa hanya mengubahnya menjadi Stringseperti yang Anda lakukan.

Jika Anda memerlukan String, maka Anda harus memformatnya byte[]dengan cara yang dapat direpresentasikan sebagai String(jika tidak, simpan saja byte[]).

Dua cara umum untuk mewakili byte[]karakter sewenang - wenang sebagai karakter yang dapat dicetak adalah BASE64 atau hex-Strings sederhana (yaitu masing-masing mewakili bytedua digit heksadesimal). Sepertinya Anda mencoba menghasilkan hex-String.

Ada juga jebakan lain: jika Anda ingin mendapatkan SHA-1 dari Java String, maka Anda perlu mengubahnya Stringmenjadi yang byte[]pertama (karena input dari SHA-1 byte[]juga). Jika Anda hanya menggunakan myString.getBytes()seperti yang Anda tunjukkan, maka itu akan menggunakan pengkodean default platform dan karenanya akan bergantung pada lingkungan tempat Anda menjalankannya (misalnya, itu bisa mengembalikan data yang berbeda berdasarkan pengaturan bahasa / lokal OS Anda).

Sebuah solusi yang lebih baik adalah untuk menetapkan pengkodean untuk digunakan untuk String-to- byte[]konversi seperti ini: myString.getBytes("UTF-8"). Memilih UTF-8 (atau penyandian lain yang dapat mewakili setiap karakter Unicode) adalah pilihan paling aman di sini.

Joachim Sauer
sumber
27

Ini adalah solusi sederhana yang dapat digunakan saat mengonversi string ke format hex:

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
sumber
Peringatan: Pembuatan hash salah untuk hash yang dimulai dengan '0'. Anda akan menerima sebuah String dengan 39 karakter.
philn
@ philn, bisakah Anda menyarankan solusi?
Nikita Koksharov
1
Saya kira jika Anda membuat bilangan bulat besar dari byte [] dengan nol di depan yang cukup, 0s ini akan hilang. Dengan demikian, representasi string hex "0" tidak akan ada di sana, mengarah ke hash dengan 39 karakter atau kurang. Saya menggunakan solusi petrnohejls di atas dan berfungsi dengan baik ...
philn
25

Cukup gunakan pustaka codec apache commons. Mereka memiliki kelas utilitas yang disebut DigestUtils

Tidak perlu masuk ke detail.

DaTroop
sumber
51
Saya tidak setuju, masuk ke detail adalah semacam titik keseluruhan
ninesided
12
Pertanyaannya adalah apakah Anda punya waktu untuk masuk ke detail atau tidak. Intinya adalah menyelesaikannya tepat waktu. Tidak semua orang adalah mahasiswa atau memiliki kemewahan untuk mempelajari semua detail.
DaTroop
DigestUtils mengembalikan array byte sehingga untuk mendapatkan representasi string Anda perlu menjalankannya melalui Hex.encodeHexString. Java: ini 2014 dan kami masih belum memiliki metode satu langkah sha
ryber
5
Satu langkah metode SHA-1:; String result = DigestUtils.sha1Hex("An input string")o)
Jon Onstott
18

Seperti yang disebutkan sebelumnya gunakan apache commons codec. Ini direkomendasikan oleh Spring guys juga (lihat DigestUtils di Spring doc). Misalnya:

DigestUtils.sha1Hex(b);

Jelas tidak akan menggunakan jawaban berperingkat teratas di sini.

Kazuar
sumber
7

Ini tidak mencetak dengan benar karena Anda harus menggunakan pengkodean Base64. Dengan Java 8 Anda dapat menyandikan menggunakan kelas encoder Base64 .

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

Hasil

Ini akan memberi Anda hasil yang diharapkan dari 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

Eduardo Dennis
sumber
1
@Devenv itu SHA-1 tiga titik berarti akan mempertahankan kode aslinya yang dikonversi ke sha1. Masalah asli OP adalah ketika mencetak string dengan benar.
Eduardo Dennis
4

Pesan Intisari (hash) adalah byte [] dalam byte [] keluar

Intisari pesan didefinisikan sebagai fungsi yang mengambil array byte mentah dan mengembalikan array byte mentah (alias byte[]). Misalnya SHA-1 (Secure Hash Algorithm 1) memiliki ukuran digest 160 bit atau 20 byte. Array byte mentah biasanya tidak dapat diartikan sebagai pengkodean karakter seperti UTF-8 , karena tidak setiap byte dalam setiap order adalah legal yang encoding. Jadi mengubahnya menjadi String:

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

mungkin membuat beberapa urutan ilegal atau memiliki penunjuk kode ke pemetaan Unicode yang tidak terdefinisi :

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

Encoding biner-ke-teks

Untuk itu digunakan pengkodean biner-ke-teks . Dengan hash, yang paling banyak digunakan adalah penyandian HEX atau Base16 . Pada dasarnya satu byte dapat memiliki nilai dari 0ke 255(atau -128ke 127ditandatangani) yang setara dengan representasi HEX dari 0x00- 0xFF. Oleh karena itu hex akan menggandakan panjang output yang diperlukan, itu berarti output 20 byte akan membuat string hex panjang 40 karakter, misalnya:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Perhatikan bahwa tidak perlu menggunakan pengkodean hex. Anda juga bisa menggunakan sesuatu seperti base64 . Hex sering disukai karena lebih mudah dibaca oleh manusia dan memiliki panjang keluaran yang ditentukan tanpa perlu bantalan.

Anda dapat mengonversi array byte ke hex dengan fungsi JDK saja:

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

Namun perlu dicatat bahwa BigIntegerakan menafsirkan array byte yang diberikan sebagai angka dan bukan sebagai string byte. Itu berarti nol di depan tidak akan dikeluarkan dan string yang dihasilkan mungkin lebih pendek dari 40 karakter.

Menggunakan Perpustakaan untuk Menyandikan ke HEX

Anda sekarang dapat menyalin dan menempelkan metode byte-to-hex yang belum diuji dari Stack Overflow atau menggunakan dependensi besar seperti Guava .

Untuk memiliki solusi masuk untuk sebagian besar masalah terkait byte, saya menerapkan utilitas untuk menangani kasus ini: bytes-java (Github)

Untuk mengonversi array byte intisikan pesan Anda, Anda cukup melakukannya

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

atau Anda bisa menggunakan fitur hash bawaan

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

Base 64 Representasi SHA1Hash:

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

Alasan ini tidak berhasil adalah ketika Anda menelepon String(md.digest(convertme)), Anda memberi tahu Java untuk menafsirkan urutan byte terenkripsi sebagai String. Yang Anda inginkan adalah mengubah byte menjadi karakter heksadesimal.

Zarkonnen
sumber
0

Konversi byte array menjadi hex string.

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
sumber