การแฮช MD5 ใน Android


92

ฉันมีไคลเอนต์ Android ที่เรียบง่ายซึ่งต้องการ 'พูดคุย' กับผู้ฟัง C # HTTP แบบธรรมดา ฉันต้องการให้การรับรองความถูกต้องระดับพื้นฐานโดยการส่งชื่อผู้ใช้ / รหัสผ่านในคำขอ POST

การแฮช MD5 นั้นไม่สำคัญใน C # และให้ความปลอดภัยเพียงพอสำหรับความต้องการของฉัน แต่ฉันไม่สามารถหาวิธีทำสิ่งนี้ได้ในตอนท้ายของ Android

แก้ไข: เพื่อจัดการกับข้อกังวลที่เกิดขึ้นเกี่ยวกับจุดอ่อนของ MD5 - เซิร์ฟเวอร์ C # ทำงานบนพีซีของผู้ใช้ไคลเอนต์ Android ของฉัน ในหลาย ๆ กรณีพวกเขาจะเข้าถึงเซิร์ฟเวอร์โดยใช้ wi-fi บน LAN ของตนเอง แต่พวกเขาอาจเลือกที่จะเข้าถึงเซิร์ฟเวอร์จากอินเทอร์เน็ตด้วยความเสี่ยง นอกจากนี้บริการบนเซิร์ฟเวอร์จำเป็นต้องใช้การส่งผ่านสำหรับ MD5 ไปยังแอปพลิเคชันของบุคคลที่สามที่ฉันไม่สามารถควบคุมได้


6
ห้ามใช้ MD5 ใช้ SHA512
SLaks

1
ทำไม? SHA512 ไม่ยากไปกว่า MD5 คุณไม่ต้องการติดอยู่ในห้าปีนับจากนี้กับไคลเอนต์เดิมที่ใช้ MD5
SLaks

2
ฉันหวังว่าคุณจะใช้ nonce ในโปรโตคอลของคุณเพื่อให้คุณสามารถกำจัดการโจมตีซ้ำได้
sarnold

1
@NickJohnson: เพื่อตอบคำถามของคุณทำไมคุณถึงจงใจเลือกตัวเลือกที่อ่อนแอกว่า? กับคำถามอื่น ... ทำไมคุณรู้สึกว่าต้องแสดงความคิดเห็นกับคำถามที่ฉันโพสต์เมื่อ 16 เดือนที่แล้ว? แต่ถ้าคุณต้องการทราบจริงๆ (ถ้าคุณดูความคิดเห็นเกี่ยวกับ SLaks ด้านบนของคุณ) มันเป็นรหัสขั้นตอนอัลฟาและส่วนท้ายของพีซี (ไม่ได้เขียนโดยฉัน) ใช้การแฮช MD5 ข้อกำหนดนี้มีไว้สำหรับสถานการณ์การส่งผ่านโดยไม่เกี่ยวข้องกับความซับซ้อนเพิ่มเติม ฉันมีผู้ทดสอบขั้นอัลฟ่าประมาณ 10 คนในเวลานั้นที่รู้ถึงความเสี่ยง การรักษาความปลอดภัยที่ซับซ้อนมากขึ้นถูกรวมเข้าด้วยกันตั้งแต่ฉันถามคำถาม
Squonk

1
...อะไร? ไม่ไม่ใช่แค่ผิด แต่ผิดอย่างร้ายแรง
Nick Johnson

คำตอบ:


232

นี่คือการนำไปใช้งานที่คุณสามารถใช้ได้ (อัปเดตเพื่อใช้อนุสัญญา Java ที่ทันสมัยมากขึ้น - for:eachวนซ้ำStringBuilderแทน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 "";
}

แม้ว่าจะไม่แนะนำให้ใช้กับระบบที่เกี่ยวข้องกับระดับความปลอดภัยขั้นพื้นฐาน (MD5 ถือว่าเสียและสามารถใช้ประโยชน์ได้ง่าย ) บางครั้งก็เพียงพอสำหรับงานพื้นฐาน


ขอบคุณที่จะทำเคล็ดลับ ดูการแก้ไขของฉันว่าทำไม MD5 ถึงเพียงพอในขั้นตอนนี้
Squonk

7
ลงคะแนน IMO เพื่อให้คำตอบที่ถูกต้องมากขึ้นด้านล่างจะปรากฏขึ้น
Adam

4
0 ไม่รองรับตามคำตอบด้านล่าง
SohailAziz

อัปเดตเพื่อใช้มาตรฐาน java ที่ใหม่กว่า (สำหรับ: แต่ละ StringBuilder)
loeschg

3
มีระบบปฏิบัติการ Android ใดบ้างที่ยังไม่ได้ใช้ MD5 (ทำให้เกิดการขว้างปาNoSuchAlgorithmException) หรือไม่?
VSB

51

คำตอบที่ยอมรับใช้ไม่ได้กับฉันใน Android 2.2 ฉันไม่รู้ว่าทำไม แต่มันคือ "กิน" ศูนย์บางส่วนของฉัน (0) Apache commonsไม่ทำงานบน Android 2.2 เนื่องจากใช้วิธีการที่รองรับโดยเริ่มจาก Android 2.3.x เท่านั้น นอกจากนี้หากคุณต้องการเพียง MD5 สตริงคอมมอนของ Apache ก็ซับซ้อนเกินไปสำหรับสิ่งนั้น ทำไมเราควรเก็บทั้งไลบรารีไว้ใช้ฟังก์ชันเล็ก ๆ จากมัน ...

ในที่สุดฉันก็พบข้อมูลโค้ดต่อไปนี้ที่นี่ซึ่งทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน หวังว่าจะเป็นประโยชน์สำหรับใครบางคน ...

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
ทำงานให้ฉัน ฉันได้เปลี่ยนmd5.getBytes ( "UTF-8") ผมได้ตรวจสอบกับ: q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68Wด้วยรหัสข้างต้นผลที่ได้คือ0c70bb931f03b75af1591f261eb77d0bไม่c70bb931f03b75af1591f261eb77d0b 0 อยู่ในสถานที่
Inoy

29

โค้ด androidsnippets.com ทำงานไม่น่าเชื่อถือเนื่องจาก 0 ดูเหมือนจะถูกตัดออกจากแฮชที่เป็นผลลัพธ์

การดำเนินที่ดีคือที่นี่

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

"m" จะเป็นโมฆะบน Android ได้ไหมในกรณีนี้
นักพัฒนา Android

3
@androiddeveloper คุณพูดถูกทั้งหมด บล็อกการจับนั้นใช้งานได้ไม่ดีที่นี่จำเป็นต้องมีคำสั่งส่งคืนหรือจะมีข้อผิดพลาดอ้างอิงที่เป็นโมฆะเมื่อมีการเรียก m.update ฉันไม่แน่ใจเช่นกัน แต่ในขณะที่สิ่งนี้อาจไม่ตัด 0 ในแต่ละไบต์ฉันคิดว่ามันอาจจะยังคงตัดการนำ 0 ออกจากทั้ง 32 char hash แทนที่จะเป็น toString (16) ฉันคิดว่า String.Format ("% 032X", bigInt) ทำงานได้ตามที่ตั้งใจไว้ นอกจากนี้คุณยังสามารถเลือกได้ว่าต้องการให้เลขฐานสิบหกเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็ก ("% 032x" สำหรับตัวล่าง)
rsimp

1
เวอร์ชันนี้มีข้อบกพร่อง หาก MD5 ของคุณเริ่มต้นด้วย "0" MD5 ที่สร้างขึ้นจะไม่มี 0 นำหน้าโปรดอย่าใช้วิธีนี้
elcuco

21

หากใช้ Apache Commons Codec เป็นตัวเลือกนี่จะเป็นการใช้งานที่สั้นกว่า:

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

หรือ SHA:

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

ที่มาสำหรับด้านบน

โปรดไปที่ลิงค์และโหวตวิธีแก้ปัญหาของเขาเพื่อให้รางวัลแก่บุคคลที่ถูกต้อง


ลิงค์ Maven repo: https://mvnrepository.com/artifact/commons-codec/commons-codec

การพึ่งพา Maven ปัจจุบัน (ณ วันที่ 6 กรกฎาคม 2559):

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

1
เหตุใดคุณจึงใช้ไลบรารีภายนอกสำหรับ API ที่มีอยู่แล้วในไลบรารีมาตรฐาน
m0skit0

12

วิธีแก้ปัญหาข้างต้นโดยใช้ DigestUtils ไม่ได้ผลสำหรับฉัน ใน Apache commons เวอร์ชันของฉัน (เวอร์ชันล่าสุดสำหรับปี 2013) ไม่มีคลาสดังกล่าว

ฉันพบวิธีแก้ปัญหาอื่นที่นี่ในบล็อกเดียว ทำงานได้อย่างสมบูรณ์แบบและไม่จำเป็นต้องใช้คอมมอนของ Apache ดูเหมือนสั้นกว่าโค้ดในคำตอบที่ยอมรับด้านบนเล็กน้อย

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

คุณจะต้องนำเข้าเหล่านี้:

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

สิ่งสำคัญที่ต้องทราบ id แฮช MD5 มีความยาว 32 ตัวอักษรหรือน้อยกว่าขอบคุณ!
Pelanes

3
ไม่กี่บรรทัดสุดท้ายอาจถูกแทนที่ด้วย: return String.Format ("% 032X", number); อย่างอื่นฉันชอบคำตอบนี้มาก
rsimp

10

นี่เป็นรูปแบบเล็กน้อยของคำตอบของ Andranik และ Den Delimarsky ข้างต้น แต่กระชับกว่าเล็กน้อยและไม่ต้องใช้ตรรกะระดับบิต แต่จะใช้String.formatวิธีการในตัวเพื่อแปลงไบต์เป็นสตริงเลขฐานสิบหกสองอักขระ ปกติฉันจะแสดงความคิดเห็นกับคำตอบของพวกเขา แต่ฉันไม่มีชื่อเสียงที่จะทำเช่นนั้น

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

หากคุณต้องการส่งคืนสตริงตัวพิมพ์เล็กแทนให้เปลี่ยน%02Xเป็น%02x.

แก้ไข: การใช้ BigInteger เช่นเดียวกับคำตอบของ wzbozon คุณสามารถทำให้คำตอบกระชับยิ่งขึ้น:

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

ฉันสร้างห้องสมุดง่ายๆใน Kotlin

เพิ่มที่ Root build.gradle

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

ที่ App build.gradle

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

การใช้งาน

ตั้งอยู่ใน Kotlin

val ob = Hasher()

จากนั้นใช้วิธีแฮช ()

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

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

มันจะส่งคืน MD5 และ SHA-1 ตามลำดับ

ข้อมูลเพิ่มเติมเกี่ยวกับห้องสมุด

https://github.com/ihimanshurawat/Hasher


3

นี่คือเวอร์ชัน Kotlin จากคำตอบของ @Andranik เราจำเป็นต้องเปลี่ยนgetBytesเป็นtoByteArray(ไม่จำเป็นต้องเพิ่ม charset UTF-8 เนื่องจากชุดอักขระเริ่มต้นtoByteArrayคือ UTF-8) และ cast array [i] เป็นจำนวนเต็ม

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
}

หวังว่ามันจะช่วยได้


1

ในแอปพลิเคชัน MVC ของเราเราสร้างสำหรับพารามิเตอร์แบบยาว

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

และเหมือนกันในแอปพลิเคชัน Android (thenk ช่วย 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

ฉันใช้วิธีการด้านล่างเพื่อให้ md5 แก่ฉันโดยส่งสตริงที่คุณต้องการรับ 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

โปรดใช้ SHA-512, MD5 ไม่ปลอดภัย

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 เป็นบิตเก่า SHA-1 เป็นขั้นตอนวิธีการที่ดีกว่ามีเป็นตัวอย่างที่นี่

( ยังเป็นที่ที่พวกเขาทราบว่าในโพสต์, Java จัดการนี้เป็นของตัวเอง, รหัสเฉพาะไม่มี Android. )


4
ไม่ - ฉันไม่ต้องการทางเลือกอื่นแทน MD5 เมื่อฉันถามคำถามนี้ในเดือนมกราคม 2011 (19 เดือนที่แล้ว) และฉันไม่แน่ใจว่าทำไมคุณถึงรู้สึกว่าจำเป็นต้องตอบคำถามของฉัน ณ จุดนี้
Squonk

21
@Squonk ฉันตอบกลับเพราะนั่นเป็นแนวคิดทั่วไปที่อยู่เบื้องหลัง stackoverflow ไม่ว่าจะผ่านไปนานแค่ไหนให้พยายามหาคำตอบที่ดีกว่าสำหรับคนที่อาจเจอคำถามในภายหลัง สำหรับการแนะนำ SHA-1 ไม่มีทางที่ฉันจะรู้ว่าคุณต่อต้าน SHA-1 โดยเฉพาะ แต่คนอื่น ๆ จะไม่เป็นเช่นนั้นอีกครั้งอาจช่วยคนอื่น ๆ ในอนาคตที่เจอสิ่งนี้และชี้นำพวกเขา ไปสู่อัลกอริทึมที่ทันสมัยกว่า
Adam

3
ฉันไม่สามารถนึกถึงสถานการณ์เดียวที่ MD5 เป็นตัวเลือกที่ไม่ดี แต่ SHA1 เป็นสิ่งที่ดี หากคุณต้องการความต้านทานการชนคุณต้องใช้ SHA2 ไม่ใช่ SHA1 หากคุณแฮชรหัสผ่านคุณต้องมี bcrypt หรือ PBKDF2 หรือในกรณีของ OP การแก้ปัญหาที่เหมาะสมอาจเป็น SSL
CodesInChaos

3
@Adam นี่ไม่ใช่คำตอบนี่คือความคิดเห็น
Antzi

0

การแปลง toHex () ที่สิ้นเปลืองเกินไปจะมีผลเหนือกว่าคำแนะนำอื่น ๆ จริงๆ

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

คำตอบนั้นเกี่ยวกับ toHex ที่ "ดีกว่า" เมื่อภารกิจนั้นเกี่ยวกับ MD5 ดังนั้นจึงไม่ตอบสนอง หมายเหตุ: Donald Knuth ปัญหาที่แท้จริงคือโปรแกรมเมอร์ใช้เวลามากเกินไปในการกังวลเกี่ยวกับประสิทธิภาพในสถานที่ที่ไม่ถูกต้องและในเวลาที่ไม่ถูกต้อง การเพิ่มประสิทธิภาพก่อนกำหนดเป็นรากเหง้าของความชั่วร้ายทั้งหมด (หรืออย่างน้อยที่สุดก็คือ) ในการเขียนโปรแกรม
zaph

0

มันทำงานได้ดีสำหรับฉันฉันใช้สิ่งนี้เพื่อรับ MD5 บน LIST Array (จากนั้นแปลงเป็นออบเจ็กต์ JSON) แต่ถ้าคุณต้องการใช้กับข้อมูลของคุณเท่านั้น พิมพ์รูปแบบแทนที่ JsonObject ด้วยของคุณ

โดยเฉพาะอย่างยิ่งถ้าคุณไม่ตรงกันกับการใช้งาน python MD5 ให้ใช้สิ่งนี้!

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;


}

0

ตัวอย่างฟังก์ชันส่วนขยายของ Kotlin ที่มีประโยชน์

fun String.toMD5(): String {
    val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray())
    return bytes.toHex()
}

fun ByteArray.toHex(): String {
    return joinToString("") { "%02x".format(it) }
}

-1

คำตอบสำหรับภาษา Scala (สั้นกว่าเล็กน้อย):

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
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.