Băm MD5 trong Android


91

Tôi có một ứng dụng khách Android đơn giản cần 'nói chuyện' với trình nghe C # HTTP đơn giản. Tôi muốn cung cấp mức xác thực cơ bản bằng cách chuyển tên người dùng / mật khẩu trong các yêu cầu ĐĂNG.

Hàm băm MD5 là tầm thường trong C # và cung cấp đủ bảo mật cho nhu cầu của tôi nhưng dường như tôi không thể tìm thấy cách thực hiện việc này ở phần cuối Android.

CHỈNH SỬA: Chỉ để giải quyết những lo ngại về điểm yếu của MD5 - máy chủ C # chạy trên PC của người dùng ứng dụng khách Android của tôi. Trong nhiều trường hợp, họ sẽ truy cập vào máy chủ bằng wi-fi trên mạng LAN của riêng họ, nhưng họ có thể tự chịu rủi ro, có thể chọn truy cập từ internet. Ngoài ra, dịch vụ trên máy chủ cần sử dụng tính năng truyền cho MD5 tới ứng dụng của bên thứ ba mà tôi không có quyền kiểm soát.


6
Không sử dụng MD5. Sử dụng SHA512.
SLaks

1
Tại sao? SHA512 không khó hơn MD5. Bạn không muốn bị mắc kẹt trong 5 năm kể từ bây giờ với các máy khách cũ sử dụng MD5.
SLaks

2
Tôi hy vọng bạn đang sử dụng nonce trong giao thức của mình, vì vậy bạn có thể loại bỏ các cuộc tấn công phát lại.
sarnold

1
@NickJohnson: Để trả lời câu hỏi của bạn tại sao bạn lại cố tình chọn tùy chọn yếu hơn? với một câu hỏi khác ... tại sao bạn cảm thấy cần phải bình luận về câu hỏi mà tôi đã đăng cách đây 16 tháng? Nhưng nếu bạn thực sự muốn biết (nếu bạn nhìn vào bình luận cho SLaks phía trên của bạn), thì đó là mã giai đoạn alpha và phần cuối PC (không phải do tôi viết) đã sử dụng băm MD5. Yêu cầu về cơ bản là cho một kịch bản chuyển qua mà không liên quan đến sự phức tạp. Lúc đó tôi có khoảng 10 người thử nghiệm giai đoạn alpha, những người biết rủi ro. Bảo mật phức tạp hơn đã được kết hợp kể từ khi tôi đặt câu hỏi.
Squonk

1
...gì? Không, điều đó không chỉ sai mà còn sai một cách nguy hiểm.
Nick Johnson

Câu trả lời:


231

Đây là một triển khai bạn có thể sử dụng (được cập nhật để sử dụng nhiều quy ước Java cập nhật hơn - for:eachvòng lặp, StringBuilderthay vì StringBuffer):

public static String md5(final String s) {
    final String MD5 = "MD5";
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest
                .getInstance(MD5);
        digest.update(s.getBytes());
        byte messageDigest[] = digest.digest();

        // Create Hex String
        StringBuilder hexString = new StringBuilder();
        for (byte aMessageDigest : messageDigest) {
            String h = Integer.toHexString(0xFF & aMessageDigest);
            while (h.length() < 2)
                h = "0" + h;
            hexString.append(h);
        }
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

Mặc dù nó không được khuyến khích cho các hệ thống liên quan đến mức bảo mật cơ bản (MD5 được coi là bị hỏng và có thể dễ dàng bị khai thác ), nhưng đôi khi nó vẫn đủ cho các tác vụ cơ bản.


Cảm ơn điều đó sẽ thực hiện thủ thuật. Xem chỉnh sửa của tôi để biết lý do tại sao MD5 sẽ đủ ở giai đoạn này.
Squonk

6
Bỏ phiếu IMO này xuống để các câu trả lời đúng hơn bên dưới sẽ xuất hiện.
Adam

4
0 không được phục vụ, như được trả lời bên dưới.
SohailAziz 19/12/12

Đã cập nhật để sử dụng các tiêu chuẩn java mới hơn (cho: mỗi, StringBuilder)
loeschg

3
Có bất kỳ hệ điều hành Android nào chưa thực hiện MD5 (nguyên nhân ném NoSuchAlgorithmException) không?
VSB

50

Câu trả lời được chấp nhận không hoạt động với tôi trong Android 2.2. Tôi không biết tại sao, nhưng nó đã "ăn" một số số 0 của tôi (0). Apache commons cũng không hoạt động trên Android 2.2, vì nó sử dụng các phương pháp chỉ được hỗ trợ bắt đầu từ Android 2.3.x. Ngoài ra, nếu bạn chỉ muốn MD5 là một chuỗi, dấu phẩy Apache quá phức tạp cho điều đó. Tại sao người ta nên giữ cả một thư viện để chỉ sử dụng một chức năng nhỏ từ nó ...

Cuối cùng tôi đã tìm thấy đoạn mã sau đây hoạt động hoàn hảo cho tôi. Tôi hy vọng nó sẽ hữu ích cho ai đó ...

public String MD5(String md5) {
   try {
        java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        byte[] array = md.digest(md5.getBytes("UTF-8"));
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < array.length; ++i) {
          sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
       }
        return sb.toString();
    } catch (java.security.NoSuchAlgorithmException e) {
    } catch(UnsupportedEncodingException ex){
    }
    return null;
}

4
Đã làm cho tôi. Tôi đã thay đổi md5.getBytes ("UTF-8") . Tôi đã kiểm tra nó với: q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68W với đoạn mã trên, kết quả là 0c70bb931f03b75af1591f261eb77d0b , KHÔNG PHẢI là c70bb931f03b75af1591f261eb77d0b . 0 được đưa ra
Inoy

28

Mã androidsnippets.com không hoạt động đáng tin cậy vì số 0 dường như bị cắt ra khỏi hàm băm kết quả.

Một triển khai tốt hơn là ở đây .

public static String MD5_Hash(String s) {
    MessageDigest m = null;

    try {
            m = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
    }

    m.update(s.getBytes(),0,s.length());
    String hash = new BigInteger(1, m.digest()).toString(16);
    return hash;
}

Trong trường hợp này, "m" có thể rỗng trên Android không?
nhà phát triển android

3
@androiddeveloper Bạn hoàn toàn đúng. Khối bắt được triển khai kém ở đây, cần phải có một câu lệnh trả về nếu không sẽ có lỗi tham chiếu null khi m.update được gọi. Ngoài ra, tôi không chắc chắn nhưng trong khi điều này có thể không loại bỏ số 0 trên từng byte riêng lẻ, tôi nghĩ rằng nó vẫn có thể loại bỏ số 0 đứng đầu trên toàn bộ 32 ký tự băm. Thay vì toString (16), tôi nghĩ rằng String.Format ("% 032X", bigInt) hoạt động như dự định. Ngoài ra, bạn có thể chọn xem bạn muốn chữ viết hoa hay chữ thường ("% 032x" cho chữ thường).
rsimp

1
Phiên bản này là thiếu sót. Nếu MD5 của bạn bắt đầu bằng "0", MD5 được tạo sẽ không có số 0. Vui lòng không sử dụng giải pháp này.
elcuco

20

Nếu sử dụng Apache Commons Codec là một tùy chọn, thì đây sẽ là cách triển khai ngắn hơn:

String md5Hex = new String(Hex.encodeHex(DigestUtils.md5(data)));

Hoặc SHA:

String shaHex= new String(Hex.encodeHex(DigestUtils.sha("textToHash")));

Nguồn cho trên.

Vui lòng theo liên kết và ủng hộ giải pháp của anh ấy để trao giải cho người phù hợp.


Liên kết kho Maven: https://mvnrepository.com/artifact/commons-codec/commons-codec

Sự phụ thuộc Maven hiện tại (kể từ ngày 6 tháng 7 năm 2016):

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>

1
Bất kỳ lý do nào tại sao bạn lại sử dụng thư viện bên ngoài cho một API đã tồn tại trong thư viện chuẩn?
m0skit0 19/1217

12

Giải pháp ở trên sử dụng DigestUtils không phù hợp với tôi. Trong phiên bản Apache commons của tôi (phiên bản mới nhất cho năm 2013) không có lớp này.

Tôi đã tìm thấy một giải pháp khác ở đây trong một blog . Nó hoạt động hoàn hảo và không cần dấu phẩy Apache. Nó trông ngắn hơn một chút so với mã trong câu trả lời được chấp nhận ở trên.

public static String getMd5Hash(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] messageDigest = md.digest(input.getBytes());
        BigInteger number = new BigInteger(1, messageDigest);
        String md5 = number.toString(16);

        while (md5.length() < 32)
            md5 = "0" + md5;

        return md5;
    } catch (NoSuchAlgorithmException e) {
        Log.e("MD5", e.getLocalizedMessage());
        return null;
    }
}

Bạn sẽ cần những lần nhập sau:

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

Điều quan trọng cần biết là mã băm MD5 có độ dài 32 ký tự trở xuống, cảm ơn bạn!
Pelanes

3
Một vài dòng cuối cùng có thể chỉ được thay thế bằng: return String.Format ("% 032X", number); Nếu không, tôi thực sự thích câu trả lời này.
rsimp

10

Đây là một biến thể nhỏ của các câu trả lời Andranik và Den Delimarsky ở trên, nhưng ngắn gọn hơn một chút và không yêu cầu bất kỳ logic nào. Thay vào đó, nó sử dụng String.formatphương thức tích hợp để chuyển đổi các byte thành hai chuỗi thập lục phân ký tự (không dải số 0). Thông thường tôi sẽ chỉ nhận xét về câu trả lời của họ, nhưng tôi không có danh tiếng để làm như vậy.

public static String md5(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");

        StringBuilder hexString = new StringBuilder();
        for (byte digestByte : md.digest(input.getBytes()))
            hexString.append(String.format("%02X", digestByte));

        return hexString.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}

Nếu bạn muốn trả về một chuỗi chữ thường thay thế, thì chỉ cần thay đổi %02Xthành %02x.

Chỉnh sửa: Sử dụng BigInteger như với câu trả lời của wzbozon, bạn có thể làm cho câu trả lời ngắn gọn hơn nữa:

public static String md5(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        BigInteger md5Data = new BigInteger(1, md.digest(input.getBytes()));
        return String.Format("%032X", md5Data);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}

4

Tôi đã tạo một Thư viện đơn giản ở Kotlin.

Thêm tại Root build.gradle

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

tại App build.gradle

implementation 'com.github.1AboveAll:Hasher:-SNAPSHOT'

Sử dụng

Trong Kotlin

val ob = Hasher()

Sau đó, sử dụng phương thức hash ()

ob.hash("String_You_Want_To_Encode",Hasher.MD5)

ob.hash("String_You_Want_To_Encode",Hasher.SHA_1)

Nó sẽ trả về MD5 và SHA-1 một cách tương ứng.

Thông tin thêm về Thư viện

https://github.com/ihimanshurawat/Hasher


2

Đây là phiên bản Kotlin từ câu trả lời @Andranik. Chúng ta cần thay đổi getBytesthành toByteArray(không cần thêm bộ mã UTF-8 vì bộ mã mặc định của toByteArraylà UTF-8) và ép kiểu mảng [i] thành số nguyên

fun String.md5(): String? {
    try {
        val md = MessageDigest.getInstance("MD5")
        val array = md.digest(this.toByteArray())
        val sb = StringBuffer()
        for (i in array.indices) {
            sb.append(Integer.toHexString(array[i].toInt() and 0xFF or 0x100).substring(1, 3))
        }
        return sb.toString()
    } catch (e: java.security.NoSuchAlgorithmException) {
    } catch (ex: UnsupportedEncodingException) {
    }
    return null
}

Hy vọng nó sẽ giúp


1

Trong ứng dụng MVC của chúng tôi, chúng tôi tạo thông số dài

using System.Security.Cryptography;
using System.Text;
    ...
    public static string getMD5(long id)
    {
        // convert
        string result = (id ^ long.MaxValue).ToString("X") + "-ANY-TEXT";
        using (MD5 md5Hash = MD5.Create())
        {
            // Convert the input string to a byte array and compute the hash. 
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(result));

            // Create a new Stringbuilder to collect the bytes and create a string.
            StringBuilder sBuilder = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
                sBuilder.Append(data[i].ToString("x2"));

            // Return the hexadecimal string. 
            result = sBuilder.ToString().ToUpper();
        }

        return result;
    }

và tương tự trong ứng dụng Android (thenk giúp Andranik)

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
...
public String getIdHash(long id){
    String hash = null;
    long intId = id ^ Long.MAX_VALUE;
    String md5 = String.format("%X-ANY-TEXT", intId);
    try {
        MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        byte[] arr = md.digest(md5.getBytes());
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < arr.length; ++i)
            sb.append(Integer.toHexString((arr[i] & 0xFF) | 0x100).substring(1,3));

        hash = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        Log.e("MD5", e.getMessage());
    }

    return hash.toUpperCase();
}

1

tôi đã sử dụng phương pháp dưới đây để cung cấp cho tôi md5 bằng cách chuyển chuỗi mà bạn muốn lấy md5

public static String getMd5Key(String password) {

//        String password = "12131123984335";

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(password.getBytes());

            byte byteData[] = md.digest();

            //convert the byte to hex format method 1
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < byteData.length; i++) {
                sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
            }

            System.out.println("Digest(in hex format):: " + sb.toString());

            //convert the byte to hex format method 2
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < byteData.length; i++) {
                String hex = Integer.toHexString(0xff & byteData[i]);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            System.out.println("Digest(in hex format):: " + hexString.toString());

            return hexString.toString();

        } catch (Exception e) {
            // TODO: handle exception
        }

        return "";
}

1

Vui lòng sử dụng SHA-512, MD5 không an toàn

public static String getSHA512SecurePassword(String passwordToHash) {
    String generatedPassword = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-512");
        md.update("everybreathyoutake".getBytes());
        byte[] bytes = md.digest(passwordToHash.getBytes());
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        generatedPassword = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return generatedPassword;
}

0

MD5 hơi cũ, SHA-1 là một thuật toán tốt hơn, có một ví dụ ở đây .

( Cũng như họ lưu ý trong bài đăng đó, Java tự xử lý điều này, không có mã cụ thể của Android. )


4
Không - Tôi không muốn các lựa chọn thay thế cho MD5 khi tôi hỏi câu hỏi này vào tháng 1 năm 2011 (19 tháng trước) và tôi không chắc tại sao bạn cảm thấy cần phải trả lời câu hỏi của tôi vào thời điểm này.
Squonk

21
@Squonk Tôi đã trả lời vì đó là ý tưởng chung đằng sau stackoverflow. Không cần biết sau thực tế bao lâu, hãy luôn cố gắng tìm câu trả lời tốt hơn cho những người có thể gặp câu hỏi sau này. Đối với việc đề xuất SHA-1, không có cách nào để tôi biết bạn đặc biệt chống lại SHA-1, nhưng nhiều người khác sẽ không như vậy, vì vậy, một lần nữa, nó có thể giúp những người khác gặp phải điều này trong tương lai và chỉ đạo họ sang một thuật toán hiện đại hơn.
Adam

3
Tôi không thể nghĩ đến một tình huống mà MD5 là một lựa chọn tồi nhưng SHA1 lại là một lựa chọn tốt. Nếu bạn cần khả năng chống va chạm, bạn cần SHA2 chứ không phải SHA1. Nếu bạn băm mật khẩu, bạn cần bcrypt hoặc PBKDF2. Hoặc trong trường hợp của OP, giải pháp thích hợp có lẽ là SSL.
CodesInChaos

3
@Adam đây không phải là câu trả lời đây là một bình luận.
Antzi

0

Chuyển đổi toHex () quá lãng phí chiếm ưu thế trong các đề xuất khác, thực sự.

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

public static String md5string(String s) {
    return toHex(md5plain(s));
}

public static byte[] md5plain(String s) {
    final String MD5 = "MD5";
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest.getInstance(MD5);
        digest.update(s.getBytes());
        return digest.digest();
    } catch (NoSuchAlgorithmException e) {
        // never happens
        e.printStackTrace();
        return null;
    }
}

public static String toHex(byte[] buf) {
    char[] hexChars = new char[buf.length * 2];
    int v;
    for (int i = 0; i < buf.length; i++) {
        v = buf[i] & 0xFF;
        hexChars[i * 2] = HEX_ARRAY[v >>> 4];
        hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

Câu trả lời là về một toHex "tốt hơn" khi nhiệm vụ là về MD5, vì vậy nó không phản hồi. Lưu ý: Donald Knuth Vấn đề thực sự là các lập trình viên đã dành quá nhiều thời gian để lo lắng về hiệu quả ở những nơi không đúng lúc và không đúng lúc; tối ưu hóa quá sớm là gốc rễ của mọi điều xấu (hoặc ít nhất là hầu hết) trong lập trình.
zaph

0

điều này đang hoạt động hoàn hảo đối với tôi, tôi đã sử dụng điều này để lấy MD5 trên Mảng LIST (sau đó chuyển đổi nó thành đối tượng JSON), nhưng nếu bạn chỉ cần áp dụng nó trên dữ liệu của mình. gõ định dạng, thay thế JsonObject bằng của bạn.

Đặc biệt nếu bạn có sự không khớp với việc triển khai python MD5, hãy sử dụng cái này!

private static String md5(List<AccelerationSensor> sensor) {

    Gson gson= new Gson();
    byte[] JsonObject = new byte[0];
    try {
        JsonObject = gson.toJson(sensor).getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    MessageDigest m = null;

    try {
        m = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    byte[] thedigest = m.digest(JsonObject);
    String hash = String.format("%032x", new BigInteger(1, thedigest));
    return hash;


}

-1

Các giải pháp được cung cấp cho ngôn ngữ Scala (ngắn hơn một chút):

def getMd5(content: Array[Byte]) =
    try {
        val md = MessageDigest.getInstance("MD5")
        val bytes = md.digest(content)
        bytes.map(b => Integer.toHexString((b + 0x100) % 0x100)).mkString
    } catch {
        case ex: Throwable => null
    }
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.