String Hash melalui SHA-256 di Java

111

Dengan melihat-lihat di sini dan juga internet secara umum, saya telah menemukan Bouncy Castle . Saya ingin menggunakan Bouncy Castle (atau beberapa utilitas lain yang tersedia secara gratis) untuk menghasilkan Hash SHA-256 dari String di Java. Melihat dokumentasi mereka, saya tidak dapat menemukan contoh bagus tentang apa yang ingin saya lakukan. Adakah yang bisa membantu saya?

knpwrs.dll
sumber

Jawaban:

264

Untuk mencirikan string, gunakan kelas MessageDigest bawaan :

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;
import java.math.BigInteger;

public class CryptoHash {
  public static void main(String[] args) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    String text = "Text to hash, cryptographically.";

    // Change this to UTF-16 if needed
    md.update(text.getBytes(StandardCharsets.UTF_8));
    byte[] digest = md.digest();

    String hex = String.format("%064x", new BigInteger(1, digest));
    System.out.println(hex);
  }
}

Dalam potongan di atas, digestberisi string berciri dan hexberisi string ASCII heksadesimal dengan bantalan nol di kiri.

Brendan Long
sumber
2
@Harry Pham, hashnya harus selalu sama, tetapi tanpa informasi lebih lanjut akan sulit untuk mengatakan mengapa Anda mendapatkan yang berbeda. Anda mungkin harus membuka pertanyaan baru.
Brendan Long
2
@Harry Pham: Setelah memanggil digestkeadaan internal diatur ulang; jadi ketika Anda memanggilnya lagi tanpa memperbarui sebelumnya, Anda mendapatkan hash dari string kosong.
Debilski
3
@BrendanLong Sekarang, Bagaimana cara mengambil digestString lagi?
Sajad
1
@Sajjad Gunakan fungsi pengkodean base64 favorit Anda. Saya pribadi suka yang ada di Apache Commons .
Brendan Long
1
@Adam Tidak, ini menyesatkan. Memanggil ´toString´ pada array byte menghasilkan hasil yang berbeda daripada mengubah isi dari array byte menjadi String, jawaban Brendan Longs lebih tepat
max.mustermann
30

Ini sudah diterapkan di runtime libs.

public static String calc(InputStream is) {
    String output;
    int read;
    byte[] buffer = new byte[8192];

    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        while ((read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
        byte[] hash = digest.digest();
        BigInteger bigInt = new BigInteger(1, hash);
        output = bigInt.toString(16);
        while ( output.length() < 32 ) {
            output = "0"+output;
        }
    } 
    catch (Exception e) {
        e.printStackTrace(System.err);
        return null;
    }

    return output;
}

Dalam lingkungan JEE6 +, seseorang juga dapat menggunakan JAXB DataTypeConverter :

import javax.xml.bind.DatatypeConverter;

String hash = DatatypeConverter.printHexBinary( 
           MessageDigest.getInstance("MD5").digest("SOMESTRING".getBytes("UTF-8")));
stacker
sumber
3
versi ini memiliki bug (setidaknya pada hari ini dan dengan java8 @ win7). coba hash '1234'. hasilnya harus dimulai dengan '03ac67 ...' tetapi sebenarnya dimulai dengan '3ac67 ...'
Chris
@Chris terima kasih, saya memperbaiki ini di sini, tetapi saya menemukan solusi yang lebih baik favorit saya saat ini: stackoverflow.com/questions/415953/…
stacker
1
Untuk pembaca baru utas lama ini: Favorit (hash MD5) yang dirujuk oleh @stacker sekarang dianggap tidak aman ( en.wikipedia.org/wiki/MD5 ).
mortensi
1
Kondisinya harus output.length () <64, bukan 32.
Sampo
Bagaimana kita bisa membandingkan dua nilai hash berbeda yang dibuat menggunakan garam yang sama? Asumsikan, satu berasal dari form login (perlu di-hash menggunakan Salt sebelum membandingkan) dan yang lainnya berasal dari db dan lalu bagaimana kita bisa membandingkan keduanya?
Pra_A
16

Anda tidak perlu perpustakaan BouncyCastle. Kode berikut menunjukkan bagaimana melakukannya menggunakan fungsi Integer.toHexString

public static String sha256(String base) {
    try{
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(base.getBytes("UTF-8"));
        StringBuffer hexString = new StringBuffer();

        for (int i = 0; i < hash.length; i++) {
            String hex = Integer.toHexString(0xff & hash[i]);
            if(hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }

        return hexString.toString();
    } catch(Exception ex){
       throw new RuntimeException(ex);
    }
}

Terima kasih khusus kepada user1452273 dari posting ini: Bagaimana cara melakukan hash beberapa string dengan sha256 di Java?

Pertahankan kerja bagus!

Pukulan cemeti
sumber
9

Saat menggunakan kode hash dengan penyedia jce apa pun, pertama-tama Anda mencoba untuk mendapatkan contoh algoritme, lalu memperbaruinya dengan data yang ingin Anda hash dan setelah selesai Anda memanggil intisari untuk mendapatkan nilai hash.

MessageDigest sha = MessageDigest.getInstance("SHA-256");
sha.update(in.getBytes());
byte[] digest = sha.digest();

Anda dapat menggunakan intisari untuk mendapatkan versi yang dikodekan base64 atau hex sesuai dengan kebutuhan Anda

Nikolaus Gradwohl
sumber
Karena penasaran, dapatkah Anda langsung digest()menggunakan array byte input, melewatkan update()?
ladenedge
Satu hal yang saya perhatikan adalah bahwa ini bekerja dengan "SHA-256" sedangkan "SHA256" melontarkan NoSuchAlgorithmException. Bukan masalah besar.
knpwrs
9
Sesuai komentar saya untuk Brandon, jangan gunakan String.getBytes()tanpa menentukan pengkodean. Saat ini kode ini dapat memberikan hasil yang berbeda pada platform yang berbeda - yang merupakan perilaku rusak untuk hash yang terdefinisi dengan baik.
Jon Skeet
@ladenedge ya - jika string Anda cukup pendek @ @KPthunder kanan Anda @Jon Skeet bergantung pada konten string - tetapi ya tambahkan string encoding getBytes agar berada di sisi penyimpanan
Nikolaus Gradwohl
7

Java 8: Base64 tersedia:

    MessageDigest md = MessageDigest.getInstance( "SHA-512" );
    md.update( inbytes );
    byte[] aMessageDigest = md.digest();

    String outEncoded = Base64.getEncoder().encodeToString( aMessageDigest );
    return( outEncoded );
Mike Dever
sumber
5

Saya kira Anda menggunakan Versi Java yang relatif lama tanpa SHA-256. Jadi, Anda harus menambahkan Penyedia BouncyCastle ke 'Penyedia Keamanan' yang sudah disediakan dalam versi java Anda.

    // NEEDED if you are using a Java version without SHA-256    
    Security.addProvider(new BouncyCastleProvider());

    // then go as usual 
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    String text = "my string...";
    md.update(text.getBytes("UTF-8")); // or UTF-16 if needed
    byte[] digest = md.digest();
obe6
sumber
+1 untuk menyebutkan BouncyCastle, yang menambahkan cukup banyak MessageDigests baru dibandingkan dengan pilihan yang relatif remeh yang tersedia di JDK.
mjuarez
0
return new String(Hex.encode(digest));
Kay
sumber
4
Tanpa info paket / penyedia kelas "Hex" tidak terlalu membantu. Dan jika itu Hex dari Apache Commons Codec, saya akan menggunakan return Hex.encodeHexString(digest)sebagai gantinya.
eckes
0

Menggunakan Java 8

MessageDigest digest = null;
try {
    digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
String encoded = DatatypeConverter.printHexBinary(hash);        
System.out.println(encoded.toLowerCase());
MST
sumber
-1

Ini akan bekerja dengan paket berikut "org.bouncycastle.util.encoders.Hex"

return new String(Hex.encode(digest));

Ada dalam toples bouncycastle.

Shrikant Karvade
sumber