Hash MD5 di Android

92

Saya memiliki klien android sederhana yang perlu 'berbicara' dengan pendengar HTTP C # sederhana. Saya ingin memberikan tingkat dasar otentikasi dengan meneruskan nama pengguna / kata sandi dalam permintaan POST.

Hash MD5 sepele di C # dan memberikan keamanan yang cukup untuk kebutuhan saya, tetapi saya tidak dapat menemukan cara melakukan ini di ujung android.

EDIT: Hanya untuk mengatasi kekhawatiran yang muncul tentang kelemahan MD5 - server C # berjalan di PC pengguna klien android saya. Dalam banyak kasus, mereka akan mengakses server menggunakan wi-fi di LAN mereka sendiri tetapi, dengan risiko mereka sendiri, mereka dapat memilih untuk mengaksesnya dari internet. Juga layanan di server perlu menggunakan pass-through untuk MD5 ke aplikasi pihak ke-3 yang tidak dapat saya kendalikan.

Squonk
sumber
6
Jangan gunakan MD5. Gunakan SHA512.
SLaks
1
Mengapa? SHA512 tidak lebih sulit dari MD5. Anda tidak ingin terjebak lima tahun dari sekarang dengan klien lama yang menggunakan MD5.
SLaks
2
Saya harap Anda menggunakan nonce dalam protokol Anda, sehingga Anda dapat membuang serangan replay.
sarnold
1
@ NickJohnson: Untuk menjawab pertanyaan Anda mengapa Anda dengan sengaja memilih opsi yang lebih lemah? dengan pertanyaan lain ... mengapa Anda merasa perlu mengomentari pertanyaan yang saya posting 16 bulan yang lalu? Tetapi jika Anda benar-benar ingin tahu (jika Anda melihat komentar untuk SLaks di atas milik Anda), itu adalah kode tahap alfa dan ujung PC (tidak ditulis oleh saya) menggunakan hashing MD5. Persyaratan tersebut pada dasarnya untuk skenario pass-through tanpa melibatkan kerumitan ekstra. Saya memiliki sekitar 10 penguji tahap alfa pada saat itu yang mengetahui risikonya. Keamanan yang lebih kompleks telah diterapkan sejak saya mengajukan pertanyaan itu.
Squonk
1
...apa? Tidak, itu tidak hanya salah, itu salah yang berbahaya.
Nick Johnson

Jawaban:

232

Berikut adalah implementasi yang dapat Anda gunakan (diperbarui untuk menggunakan lebih banyak konvensi Java terbaru - for:eachloop, StringBuilderdaripada 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 "";
}

Meskipun tidak disarankan untuk sistem yang melibatkan bahkan tingkat keamanan dasar (MD5 dianggap rusak dan dapat dengan mudah dieksploitasi ), terkadang cukup untuk tugas-tugas dasar.

Den Delimarsky
sumber
Terima kasih itu akan berhasil. Lihat suntingan saya mengapa MD5 akan cukup pada tahap ini.
Squonk
7
Pilih ini IMO sehingga jawaban yang lebih benar di bawah ini akan muncul.
Adam
4
0 tidak terpenuhi, seperti yang dijawab di bawah.
SohailAziz
Diperbarui untuk menggunakan standar java yang lebih baru (untuk: masing-masing, StringBuilder)
loeschg
3
Apakah ada sistem operasi android yang belum diimplementasikan MD5 (sebabkan dibuang NoSuchAlgorithmException)?
VSB
50

Jawaban yang diterima tidak berhasil untuk saya di Android 2.2. Saya tidak tahu mengapa, tapi itu "memakan" sebagian dari nol saya (0). Apache commons juga tidak berfungsi di Android 2.2, karena menggunakan metode yang hanya didukung mulai dari Android 2.3.x. Juga, jika Anda hanya ingin MD5 sebuah string, Apache commons terlalu rumit untuk itu. Mengapa seseorang harus menyimpan seluruh perpustakaan untuk menggunakan hanya fungsi kecil darinya ...

Akhirnya saya menemukan potongan kode berikut di sini yang bekerja dengan sempurna untuk saya. Semoga bermanfaat bagi seseorang ...

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;
}
Andranik
sumber
4
Bekerja untuk saya. Saya telah mengubah md5.getBytes ("UTF-8") . Saya sudah memeriksanya dengan: q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68W dengan kode di atas, hasilnya 0c70bb931f03b75af1591f261eb77d0b , BUKAN c70bb931f03b75af1591f261eb77d0b . 0 ada di tempat
Inoy
29

Kode androidsnippets.com tidak berfungsi dengan andal karena 0 tampaknya dipotong dari hash yang dihasilkan.

Implementasi yang lebih baik ada di sini .

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;
}
Kristen
sumber
Bisakah "m" menjadi nol di Android, dalam kasus ini?
Pengembang android
3
@androiddeveloper Anda benar sekali. Blok catch tidak diimplementasikan dengan baik di sini, perlu ada pernyataan return atau akan ada kesalahan referensi null ketika m.update dipanggil. Juga saya tidak yakin tetapi meskipun ini mungkin tidak menghapus 0 pada masing-masing byte, saya pikir itu mungkin masih menghapus 0 terkemuka pada keseluruhan 32 karakter hash. Alih-alih toString (16) saya pikir String.Format ("% 032X", bigInt) berfungsi sebagaimana mestinya. Selain itu, Anda dapat memilih apakah Anda ingin hex dalam huruf besar atau kecil ("% 032x" untuk huruf kecil).
rsimp
1
Versi ini cacat. Jika MD5 Anda dimulai dengan "0", MD5 yang dihasilkan tidak akan memiliki awalan 0. Jangan gunakan solusi ini.
elcuco
21

Jika menggunakan Apache Commons Codec adalah sebuah opsi, maka ini adalah implementasi yang lebih singkat:

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

Atau SHA:

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

Sumber di atas.

Silakan ikuti tautan dan beri suara positif solusinya untuk memberi penghargaan kepada orang yang tepat.


Tautan repo Maven: https://mvnrepository.com/artifact/commons-codec/commons-codec

Ketergantungan Maven saat ini (per 6 Juli 2016):

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
tbraun
sumber
1
Ada alasan mengapa Anda menggunakan pustaka eksternal untuk API yang sudah ada di pustaka standar?
m0skit0
12

Solusi di atas menggunakan DigestUtils tidak berhasil untuk saya. Dalam versi Apache commons saya (yang terbaru untuk 2013) tidak ada kelas seperti itu.

Saya menemukan solusi lain di sini di satu blog . Ia bekerja dengan sempurna dan tidak membutuhkan Apache commons. Ini terlihat sedikit lebih pendek dari kode dalam jawaban yang diterima di atas.

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

Anda akan membutuhkan impor ini:

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Denis Kutlubaev
sumber
Penting untuk diketahui bahwa hash MD5 memiliki panjang 32 karakter atau kurang, terima kasih!
Pelanes
3
Beberapa baris terakhir mungkin bisa diganti dengan: return String.Format ("% 032X", number); Kalau tidak, saya sangat suka jawaban ini.
rsimp
10

Ini adalah variasi kecil dari jawaban Andranik dan Den Delimarsky di atas, tetapi sedikit lebih ringkas dan tidak memerlukan logika bitwise. Sebaliknya ia menggunakan metode built-in String.formatuntuk mengubah byte menjadi dua karakter string heksadesimal (tidak menghapus 0). Biasanya saya hanya akan mengomentari jawaban mereka, tetapi saya tidak memiliki reputasi untuk melakukannya.

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

Jika Anda ingin mengembalikan string huruf kecil, ubah saja %02Xke %02x.

Sunting: Menggunakan BigInteger seperti dengan jawaban wzbozon, Anda dapat membuat jawabannya lebih ringkas:

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;
    }
}
rsimp
sumber
4

Saya telah membuat Library sederhana di Kotlin.

Tambahkan di Root build.gradle

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

di App build.gradle

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

Pemakaian

Di Kotlin

val ob = Hasher()

Kemudian Gunakan metode hash ()

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

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

Ini akan mengembalikan MD5 dan SHA-1 Masing-masing.

Lebih lanjut tentang Perpustakaan

https://github.com/ihimanshurawat/Hasher

Himanshu Rawat
sumber
3

Ini adalah versi Kotlin dari jawaban @Andranik. Kita perlu mengubah getByteske toByteArray(tidak perlu menambahkan charset UTF-8 karena charset default toByteArrayadalah UTF-8) dan cast array [i] menjadi integer

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
}

Semoga membantu

Phan Van Linh
sumber
1

Dalam aplikasi MVC kami, kami menghasilkan parameter panjang

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

dan sama di aplikasi Android (thenk membantu 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();
}
Grabets Oleg
sumber
1

saya telah menggunakan metode di bawah ini untuk memberi saya md5 dengan melewatkan string yang ingin Anda dapatkan 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 "";
}
Ghanshyam Patidar
sumber
1

Harap gunakan SHA-512, MD5 tidak aman

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;
}
Carlos Jesus Arancibia Taborga
sumber
0

MD5 agak tua, SHA-1 adalah algoritme yang lebih baik, ada contohnya di sini .

( Juga seperti yang mereka catat di posting itu, Java menangani ini sendiri, tidak ada kode khusus Android. )

Adam
sumber
4
Tidak - Saya tidak ingin alternatif selain MD5 ketika saya mengajukan pertanyaan ini pada Januari 2011 (19 bulan yang lalu) dan saya tidak yakin mengapa Anda merasa perlu untuk menanggapi pertanyaan saya pada saat ini.
Squonk
21
@Squonk Saya menjawab karena itu adalah ide umum di balik stackoverflow. Tidak peduli berapa lama setelah itu terjadi, selalu berusaha mendapatkan jawaban yang lebih baik untuk orang-orang yang mungkin menemukan pertanyaan itu nanti. Sedangkan untuk menyarankan SHA-1, tidak ada cara bagi saya untuk mengetahui bahwa Anda secara khusus menentang SHA-1, tetapi banyak yang lainnya tidak, jadi sekali lagi, ini dapat membantu orang lain di masa depan yang menemukan ini, dan mengarahkan mereka ke algoritme yang lebih modern.
Adam
3
Saya tidak dapat memikirkan satu situasi pun di mana MD5 adalah pilihan yang buruk tetapi SHA1 adalah pilihan yang baik. Jika Anda membutuhkan ketahanan tabrakan, Anda membutuhkan SHA2 bukan SHA1. Jika Anda memiliki kata sandi, Anda memerlukan bcrypt atau PBKDF2. Atau dalam kasus OP, solusi yang tepat mungkin adalah SSL.
CodesInChaos
3
@Adam ini bukan jawaban ini adalah komentar.
Antzi
0

Konversi toHex () yang terlalu boros berlaku di saran lain, sungguh.

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);
}
Gena Batsyan
sumber
Jawabannya adalah tentang toHex yang "lebih baik" jika misinya tentang MD5, karena itu tidak responsif. Catatan: Donald Knuth Masalah sebenarnya adalah para programmer menghabiskan terlalu banyak waktu untuk mengkhawatirkan efisiensi di tempat yang salah dan di waktu yang salah; optimasi prematur adalah akar dari semua kejahatan (atau setidaknya sebagian besar) dalam pemrograman.
zaf
0

ini bekerja dengan sempurna untuk saya, saya menggunakan ini untuk mendapatkan MD5 di LIST Array (kemudian mengubahnya menjadi objek JSON), tetapi jika Anda hanya perlu menerapkannya pada data Anda. ketik format, ganti JsonObject dengan milik Anda.

Terutama jika Anda memiliki ketidakcocokan dengan implementasi python MD5, gunakan ini!

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;


}
mehran sahandi jauh
sumber
0

Contoh Fungsi Ekstensi Kotlin yang Berguna

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

fun ByteArray.toHex(): String {
    return joinToString("") { "%02x".format(it) }
}
kzncrda.dll
sumber
-1

Solusi yang disediakan untuk bahasa Scala (sedikit lebih pendek):

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
    }
david.perez
sumber