Hash String qua SHA-256 trong Java


111

Bằng cách nhìn quanh đây cũng như internet nói chung, tôi đã tìm thấy Lâu đài Bouncy . Tôi muốn sử dụng Bouncy Castle (hoặc một số tiện ích có sẵn miễn phí khác) để tạo SHA-256 Hash của một chuỗi trong Java. Nhìn vào tài liệu của họ, tôi dường như không thể tìm thấy bất kỳ ví dụ tốt nào về những gì tôi muốn làm. Có ai ở đây có thể giúp tôi không?

Câu trả lời:


264

Để băm một chuỗi, hãy sử dụng lớp MessageDigest được tích hợp sẵn :

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

Trong đoạn mã trên, digestchứa chuỗi băm và hexchứa chuỗi ASCII thập lục phân với phần đệm số 0 bên trái.


2
@Harry Pham, hash luôn phải giống nhau, nhưng nếu không có thêm thông tin thì thật khó để nói tại sao bạn lại nhận được những hash khác nhau. Bạn có thể nên mở một câu hỏi mới.
Brendan Long

2
@Harry Pham: Sau khi gọi digesttrạng thái bên trong được thiết lập lại; vì vậy khi bạn gọi lại nó mà không cập nhật trước đó, bạn sẽ nhận được hàm băm của chuỗi trống.
Debilski

3
@BrendanLong Bây giờ, Làm cách nào để truy xuất digestlại chuỗi?
Sajad

1
@Sajjad Sử dụng chức năng mã hóa base64 yêu thích của bạn. Cá nhân tôi thích cái trong Apache Commons .
Brendan Long

1
@Adam Không, điều này gây hiểu lầm. Gọi ´toString´ trên một mảng byte đang tạo ra một kết quả khác với việc chuyển đổi nội dung của mảng byte thành Chuỗi, câu trả lời của Brendan Longs thích hợp hơn
max.mustermann

30

Điều này đã được thực hiện trong libs thời gian chạy.

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

Trong môi trường JEE6 +, người ta cũng có thể sử dụng JAXB DataTypeConverter :

import javax.xml.bind.DatatypeConverter;

String hash = DatatypeConverter.printHexBinary( 
           MessageDigest.getInstance("MD5").digest("SOMESTRING".getBytes("UTF-8")));

3
phiên bản này có một lỗi (ít nhất là cho đến hôm nay và với java8 @ win7). cố gắng băm '1234'. kết quả phải bắt đầu bằng '03ac67 ...' nhưng nó thực sự bắt đầu bằng '3ac67 ...'
Chris

@ Chris cảm ơn, tôi cố định này ở đây, nhưng tôi tìm thấy một giải pháp tốt hơn yêu thích của tôi về vấn đề này hiện đang: stackoverflow.com/questions/415953/...
stacker

1
Đối với độc giả mới của chủ đề cũ này: Mục yêu thích (MD5-hash) được tham chiếu bởi @stacker hiện được coi là không an toàn ( en.wikipedia.org/wiki/MD5 ).
mortensi

1
Điều kiện phải là output.length () <64, không phải 32.
Sampo

Làm thế nào chúng ta có thể so sánh hai giá trị băm khác nhau được tạo bằng cách sử dụng cùng một muối? Giả sử, một cái đến từ đăng nhập biểu mẫu (cần băm bằng cách sử dụng Salt trước khi so sánh) và cái khác đến từ db và sau đó làm thế nào chúng ta có thể so sánh hai cái này?
Pra_A

16

Bạn không nhất thiết phải cần thư viện BouncyCastle. Đoạn mã sau cho biết cách thực hiện việc này bằng cách sử dụng hàm 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);
    }
}

Đặc biệt cảm ơn người dùng1452273 từ bài đăng này: Làm thế nào để băm một số chuỗi với sha256 trong Java?

Hãy tiếp tục phát huy !


9

Khi sử dụng mã băm với bất kỳ nhà cung cấp jce nào, trước tiên bạn cố gắng lấy một phiên bản của thuật toán, sau đó cập nhật nó với dữ liệu bạn muốn được băm và khi bạn hoàn thành, bạn gọi thông báo để nhận giá trị băm.

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

bạn có thể sử dụng thông báo để nhận phiên bản mã hóa base64 hoặc hex tùy theo nhu cầu của bạn


Vì tò mò, bạn có thể đi thẳng đến digest()mảng byte đầu vào, bỏ qua update()không?
ladenedge

Một điều tôi nhận thấy là điều này hoạt động với "SHA-256" trong khi "SHA256" ném một NoSuchAlgorithmException. Không có vấn đề gì.
knpwrs

9
Theo nhận xét của tôi cho Brandon, không sử dụng String.getBytes()mà không chỉ định mã hóa. Hiện tại, mã này có thể cho các kết quả khác nhau trên các nền tảng khác nhau - đó là hành vi bị hỏng đối với một hàm băm được xác định rõ.
Jon Skeet

@ladenedge có - nếu chuỗi của bạn là enought ngắn @KPthunder ngay @ Jon Skeet bạn phụ thuộc vào nội dung của chuỗi - nhưng có thêm một chuỗi mã hóa getBytes để được vào tiết kiệm bên
Nikolaus Gradwohl

7

Java 8: Base64 khả dụng:

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

    String outEncoded = Base64.getEncoder().encodeToString( aMessageDigest );
    return( outEncoded );

5

Tôi cho rằng bạn đang sử dụng Phiên bản Java tương đối cũ không có SHA-256. Vì vậy, bạn phải thêm Nhà cung cấp BouncyCastle vào 'Nhà cung cấp bảo mật' đã được cung cấp trong phiên bản java của bạn.

    // 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();

+1 khi đề cập đến BouncyCastle, bổ sung thêm khá nhiều MessageDigest mới so với lựa chọn tương đối ít có sẵn trong JDK.
mjuarez

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

4
Nếu không có thông tin gói / nhà cung cấp, lớp "Hex" không hữu ích lắm. Và nếu đó là Hex từ Apache Commons Codec, tôi sẽ sử dụng return Hex.encodeHexString(digest)thay thế.
eckes

0

Sử dụng 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());

-1

Điều này sẽ hoạt động với gói sau "org.bouncycastle.util.encoders.Hex"

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

Của nó trong lọ bouncycastle.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.