Chuỗi Java sang SHA1


158

Tôi đang cố gắng tạo một trình chuyển đổi String sang SHA1 đơn giản trong Java và đây là những gì tôi đã có ...

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

Khi tôi vượt qua nó toSHA1("password".getBytes()), tôi [�a�ɹ??�%l�3~��.biết rằng đó có thể là một sửa lỗi mã hóa đơn giản như UTF-8, nhưng ai đó có thể cho tôi biết tôi nên làm gì để có được thứ tôi muốn 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8không? Hay tôi đang làm điều này hoàn toàn sai?


Thuật toán SHA1không có dấu gạch nối, không biết điều đó sẽ tạo ra sự khác biệt.
Scrum Meister

Đó là một thực hành tốt để chỉ định mã hóa ký tự khi bạn gọi getBytes(), ví dụ sử dụngtoSHA1("password".getBytes("UTF-8"))
Qwerky

Câu trả lời:


183

CẬP NHẬT
Bạn có thể sử dụng Apache Commons Codec (phiên bản 1.7+) để thực hiện công việc này cho bạn.

DigestUtils.sha1Hex (stringToConvertToSHexRepftimeation)

Cảm ơn @ Jon Onstott cho đề xuất này.


Câu trả lời cũ
Chuyển đổi mảng Byte của bạn thành Chuỗi Hex. Real's How để cho bạn biết làm thế nào .

return byteArrayToHexString(md.digest(convertme))

và (được sao chép từ Cách làm của Real)

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, bạn có thể nhận được đại diện nhỏ gọn hơn bằng cách sử dụng Base64. Apache Commons Codec API 1.4 , có tiện ích tuyệt vời này để loại bỏ mọi nỗi đau. tham khảo tại đây


4
base64 và sha1 rất khác nhau - không đề xuất chúng như là lựa chọn thay thế.
Ryan A.

13
@RyanA.: Theo tôi hiểu, anh ấy đề xuất base64 như một cách thay thế cho mã hóa hex băm SHA1 (không phải là một thay thế hoàn toàn cho SHA1).
helmbert

Tôi chưa thử nó, nhưng bạn có quan tâm giải thích nó hoạt động như thế nào không?
Jivay

11
Tại sao không sử dụng một thư viện như DigestUtils.sha1Hex("my string")thay vì phát minh lại bánh xe (mặc dù thật thú vị khi biết cách chuyển đổi sang hex bằng tay)?
Jon Onstott

3
Bởi vì khi câu trả lời này được viết, DigestUtils (1.7 được phát hành vào tháng 9 năm 2012) không có tính năng đó. Cảm ơn đã chỉ ra điều này. +1
Nishant

67

Đây là giải pháp chuyển đổi chuỗi của tôi sang sha1. Nó hoạt động tốt trong ứng dụng Android của tôi:

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
Có thể muốn xác định rằng đây là java.util.Formatter và cần một formatter.close () ở cuối để tránh cảnh báo.
Eric Chen

Không encryptPassword("test")echo test|sha1sumtrong thiết bị đầu cuối linux có kết quả giống nhau không? Họ không.
Tulains Córdova 17/12/14

@ TulainsCórdova Liên quan đến lệnh gọi bàn điều khiển: Nếu bạn sử dụng echo test, đầu ra bao gồm ngắt dòng sẽ được dẫn đến sha1sum. Nếu bạn muốn băm một chuỗi đơn giản mà không ngắt dòng, bạn có thể sử dụng echo -n test | sha1sum. Các -nlàm cho thông sốecho bỏ qua ngắt dòng.
MrSnrub

Ít hơn cho câu hỏi, nhưng nói chung hơn: encryptPassword()Âm thanh của bạn như thế được sử dụng để lưu trữ dữ liệu xác thực. Lưu ý rằng mã hóa của bạn dễ bị tấn công từ điển, vì không áp dụng seeding. Kiểm tra môi trường bảo mật của bạn cho dù đây là một vấn đề cho ứng dụng của bạn!
EagleRainbow

54

Sử dụng lớp Băm ổi :

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

1
Câu trả lời này có thể cần một bản cập nhật vì điều này sẽ tạo ra một cảnh báo rằng Băm không ổn định.
Semir Deljić

32

SHA-1 (và tất cả các thuật toán băm khác) trả về dữ liệu nhị phân. Điều đó có nghĩa là (trong Java) họ sản xuất a byte[]. Đó là bytemảng nào không đại diện cho bất kỳ nhân vật cụ thể, mà có nghĩa là bạn không thể chỉ đơn giản là biến nó thành một Stringnhư bạn đã làm.

Nếu bạn cần a String, thì bạn phải định dạng byte[]theo cách có thể được biểu diễn dưới dạng String(nếu không, chỉ cần giữ byte[]xung quanh).

Hai cách phổ biến để biểu diễn byte[]các ký tự có thể in là BASE64 hoặc các chuỗi hex đơn giản (nghĩa là đại diện cho mỗi bytehai chữ số thập lục phân). Có vẻ như bạn đang cố gắng tạo ra một chuỗi hex.

Ngoài ra còn có một cạm bẫy khác: nếu bạn muốn lấy SHA-1 của Java String, thì bạn cần phải chuyển đổi nó Stringthành byte[]đầu tiên (vì đầu vào của SHA-1 byte[]cũng vậy). Nếu bạn chỉ đơn giản sử dụng myString.getBytes()như bạn đã trình bày thì nó sẽ sử dụng mã hóa mặc định của nền tảng và như vậy sẽ phụ thuộc vào môi trường bạn chạy nó (ví dụ: nó có thể trả về dữ liệu khác nhau dựa trên cài đặt ngôn ngữ / ngôn ngữ của hệ điều hành của bạn).

Một giải pháp tốt hơn là chỉ định mã hóa để sử dụng cho chuyển đổi String-to- byte[]như thế này : myString.getBytes("UTF-8"). Chọn UTF-8 (hoặc một mã hóa khác có thể đại diện cho mọi ký tự Unicode) là lựa chọn an toàn nhất ở đây.


27

Đây là một giải pháp đơn giản có thể được sử dụng khi chuyển đổi chuỗi thành định dạng 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);
}

Cảnh báo: Tạo băm không chính xác cho băm bắt đầu bằng '0'. Bạn sẽ nhận được một Chuỗi có 39 ký tự.
philn

@philn bạn có thể đề xuất giải pháp?
Nikita Koksharov

1
Tôi đoán rằng nếu bạn tạo một số nguyên lớn từ một byte [] có đủ các số 0 đứng đầu, các số 0 này sẽ bị mất. Do đó, biểu diễn chuỗi hex "0" sẽ không ở đó, dẫn đến hàm băm có 39 ký tự hoặc thậm chí ít hơn. Tôi đã sử dụng giải pháp petrnohejls ở trên và nó hoạt động tốt ...
philn 2/11/2016

25

Chỉ cần sử dụng thư viện codec apache commons. Họ có một lớp tiện ích gọi là DigestUtils

Không cần phải đi vào chi tiết.


51
Tôi không đồng ý, đi vào các chi tiết là loại toàn bộ điểm
ninesided

12
Câu hỏi là liệu bạn có thời gian để đi vào chi tiết hay không. Toàn bộ vấn đề thường là hoàn thành đúng hạn. Không phải ai cũng là sinh viên hay có học thức để học mọi chi tiết.
DaTroop

DigestUtils trả về một mảng byte vì vậy để có được biểu diễn chuỗi, bạn cần chạy nó thông qua Hex.encodeHexString. Java: đó là năm 2014 và chúng tôi vẫn chưa có phương pháp sha một bước
ryber

5
Phương pháp một bước SHA-1 : String result = DigestUtils.sha1Hex("An input string"); o)
Jon Onstott

18

Như đã đề cập trước khi sử dụng codec apache commons. Nó cũng được đề xuất bởi các chàng trai mùa xuân (xem DigestUtils trong Spring doc). Ví dụ:

DigestUtils.sha1Hex(b);

Chắc chắn sẽ không sử dụng câu trả lời đánh giá hàng đầu ở đây.


6

Nó không được in chính xác vì bạn cần sử dụng mã hóa Base64. Với Java 8, bạn có thể mã hóa bằng lớp mã hóa Base64 .

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

Kết quả

Điều này sẽ cung cấp cho bạn đầu ra dự kiến ​​của bạn về 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8


1
@Devenv đó là SHA-1, ba dấu chấm có nghĩa là nó sẽ duy trì mã gốc chuyển đổi thành sha1. Vấn đề ban đầu của OP là khi in chuỗi chính xác.
Eduardo Dennis

4

Thông báo Digest (hàm băm) là byte [] in byte [] out

Một thông báo thông báo được định nghĩa là một hàm lấy một mảng byte thô và trả về một mảng byte thô (aka byte[]). Ví dụ: SHA-1 (Thuật toán băm bảo mật 1) có kích thước phân loại là 160 bit hoặc 20 byte. Mảng byte thô thường không thể được hiểu là mã hóa ký tự như UTF-8 , bởi vì không phải mọi byte trong mỗi đơn hàng đều là mã hóa hợp pháp. Vì vậy, chuyển đổi chúng thành một Stringvới:

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

có thể tạo một số chuỗi bất hợp pháp hoặc có các con trỏ mã để ánh xạ Unicode không xác định :

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

Mã hóa nhị phân thành văn bản

Đối với mã hóa nhị phân thành văn bản được sử dụng. Với băm, cái được sử dụng nhiều nhất là mã hóa HEX hoặc Base16 . Về cơ bản một byte có thể có giá trị từ 0đến 255(hoặc -128để 127ký) tương đương với các đại diện HEX của 0x00- 0xFF. Do đó hex sẽ tăng gấp đôi độ dài cần thiết của đầu ra, điều đó có nghĩa là đầu ra 20 byte sẽ tạo ra chuỗi hex dài 40 ký tự, ví dụ:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Lưu ý rằng không bắt buộc phải sử dụng mã hóa hex. Bạn cũng có thể sử dụng một cái gì đó như base64 . Hex thường được ưa thích vì con người dễ đọc hơn và có độ dài đầu ra xác định mà không cần đệm.

Bạn có thể chuyển đổi một mảng byte thành hex chỉ với chức năng JDK:

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

Tuy nhiên, lưu ý rằng BigIntegersẽ diễn giải mảng byte đã cho dưới dạng số và không phải là chuỗi byte. Điều đó có nghĩa là các số 0 đứng đầu sẽ không được xuất ra và chuỗi kết quả có thể ngắn hơn 40 ký tự.

Sử dụng Thư viện để Mã hóa cho HEX

Bây giờ bạn có thể sao chép và dán một phương thức byte-hex chưa được kiểm tra từ Stack Overflow hoặc sử dụng các phụ thuộc lớn như Guava .

Để có giải pháp xử lý cho hầu hết các vấn đề liên quan đến byte, tôi đã triển khai một tiện ích để xử lý các trường hợp sau: byte-java (Github)

Để chuyển đổi mảng byte thông báo của bạn, bạn chỉ cần làm

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

hoặc bạn chỉ có thể sử dụng tính năng băm tích hợp

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

2

Cơ sở 64 Đại diện của SHA1Hash:

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

1

Lý do điều này không hoạt động là khi bạn gọi String(md.digest(convertme)), bạn đang bảo Java diễn giải một chuỗi các byte được mã hóa thành một Chuỗi. Những gì bạn muốn là chuyển đổi các byte thành các ký tự thập lục phân.


0

Chuyển đổi mảng byte thành chuỗi hex.

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