Hỏi về HASHING MD5 trên JAVA

java

(Harry Stormborn) #1

Chào m.n, mình có dùng Java để hash 1 string sang MD5. Nhưng tham khảo code trên mạng mình thấy k hiểu lắm. Code here:

import java.security.NoSuchAlgorithmException;

public class JavaMD5 {

    public static void main(String[] args) {
        String passwordToHash = "MyPassword123";
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(passwordToHash.getBytes());
            byte[] bytes = md.digest();
            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) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(generatedPassword);
    }
}

Tại chỗ (bytes[i] & 0xff) mình thấy họ nói dùng để “ensure byte[i] is possitive”,. mình k hiểu tạo sao phải lm vậy @@. Ai giải thích giúp mình dòng đó đc k ạ?


(SITUVN.gcd) #2

Hiểu về phép toán bit nhé. Phải là nguyên đoạn (bytes[i] & 0xff) + 0x100 mới chuyển về số nguyên dương.


(Harry Stormborn) #3

Cảm ơn bạn. Mình tham khảo 1 hàm md5 khác họ làm như sau:

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

// Java program to calculate MD5 hash value 
public class MD5 {
     public static String getMd5(String input) 
     { 
         try {
             // Static getInstance method is called with hashing MD5 
             MessageDigest md = MessageDigest.getInstance( "MD5" ); 

             // digest() method is called to calculate message digest 
             //  of an input digest() return array of byte 
             byte [] messageDigest = md.digest(input.getBytes()); 

             // Convert byte array into signum representation 
             BigInteger no =  new BigInteger( 1 , messageDigest); 

             // Convert message digest into hex value 
             String hashtext = no.toString( 16 ); 

             while (hashtext.length() <  32 ) {
                 hashtext =  "0" + hashtext;
             } 

             return hashtext; 
         }  

         // For specifying wrong message digest algorithms 
         catch (NoSuchAlgorithmException e) { 
             throw new RuntimeException(e); 
         } 
     }

     // Driver code 
     public static void main(String args[])  throws NoSuchAlgorithmException 
     { 
         String s =  "GeeksForGeeks" ; 
         System.out.println( "Your HashCode Generated by MD5 is: " + getMd5(s)); 
     } 
} 

Tại sao phải cộng thêm “0” vào trc string vậy ạ?, vì cách bên trên họ k có xét length < 32 =.=


(SITUVN.gcd) #4

Để đảm bảo nó dài đủ 32 kí tự, nhưng chắc cũng dư thừa. MD5 trả về mảng 32 byte mà.


(Thai Pham) #5

Như @SITUVN.gcd đã trả lời, việc thêm chuỗi ký tự “0” vào đầu dãy hashtext để bảo đảm chuỗi MD5 cuối cùng luôn có độ dài 32 ký tự. Tuy nhiên, tôi không đồng ý với @SITUVN.gcd là việc này dư thừa vì trong một số trường hợp, hashtext có thể ít hơn 32 ký tự (Có thể kiểm tra bằng cách bỏ đoạn mã sau:

while (hashtext.length() <  32 ) {
        hashtext =  "0" + hashtext;
} 

và sửa đoạn mã trong hàm main thành như sau:

public static void main(String args[])  throws NoSuchAlgorithmException                                                                                                                                                              
{                                                                                                                                                                                                                                    
        String s =  "Geeks" ;                                                                                                                                                                                                            
        String md5Str = getMd5(s);                                                                                                                                                                                                       
        System.out.println("Your HashCode Generated by MD5 is: " + md5Str);                                                                                                                                                              
        System.out.println("MD5 string length: " + md5Str.length());                                                                                                                                                                     
}

Dễ thấy trong trường hợp này, chuỗi MD5 cuối cùng chỉ có 31 ký tự.


(Thai Pham) #7

@SITUVN.gcd giải thích chưa chính xác, thật ra thì đoạn mã

bytes[i] & 0xff

là đủ để bảo đảm rằng bytes[i] được đổi thành giá trị dương. Lý do như sau:

  • Khi bạn dùng hàm Integer.toString(), giá trị bytes[i] sẽ phải được chuyển đổi thành kiểu Int. Và vì Int dùng đến 4 bytes, quá trình này sẽ làm giá trị bytes[i] ban đầu trở thành một giá trị nguyên âm với 4 bytes thay vì 1. Cụ thể hơn, nếu chúng ta có giá trị bytes[i] ban đầu là 0x0e, quá trình đổi giá trị này từ byte sang Int sẽ đưa đến kết quả là ff ff ff 0e thay vì 00 00 00 0e. Giá trị này có thể dẫn đến những hiệu ứng không mong muốn, vì thế, chúng ta phải đảm bảo rằng 3 byte đầu luôn luôn là 00. Để có kết quả này, chúng ta cần AND bytes[i] với 0xff (hay chính xác hơn là 00 00 00 ff) để bảo đảm rằng byte cuối trong giá trị kết quả có giá trị của bytes[i] trong khi 3 bytes đầu tiên là 0.

  • Tiếp theo, nếu chúng ta đổi trực tiếp 00 00 00 0e thành ký tự mà không dùng đến thủ thuật cộng với 0x100, nó sẽ trở thành e thay vì 0e. Vì chúng ta muốn rằng mỗi byte luôn được đại diện bởi 2 ký tự hex (trong trường hợp này là 0e), chúng ta cần cộng với 0x100 để trở thành 00 00 01 0e. Khi đổi thành chuỗi, giá trị này trở thành chuỗi 10e. Và cuối cùng, chúng ta cần bỏ ký tự 1 ra khỏi chuỗi đại diện cho byte[i] vì đây chỉ là thủ thuật trung gian, vì vậy chúng ta phải thêm vào hàm substring(1) để loại bỏ nó.

Đây chỉ là một số thủ thuật lập trình để đảm bảo quá trình chuyển đổi đúng như mong muốn và không gây tác dụng phụ.

Chúc vui.


(Harry Stormborn) #8

Cảm ơn bạn rất nhiều ạ :3


83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?