98

I have a simple android client which needs to 'talk' to a simple C# HTTP listener. I want to provide a basic level of authentication by passing username/password in POST requests.

MD5 hashing is trivial in C# and provides enough security for my needs but I can't seem to find how to do this at the android end.

EDIT: Just to address the concerns raised about MD5 weakness - the C# server runs on the PCs of the users of my android client. In many cases, they'll be accessing the server using wi-fi on their own LANs but, at their own risk, they may choose to access it from the internet. Also the service on the server needs to use pass-through for the MD5 to a 3rd party application I have no control over.

Squonk
  • 48,735
  • 19
  • 103
  • 135
  • 6
    Do not use MD5. Use SHA512. – SLaks Jan 31 '11 at 00:06
  • i think this is a duplicate of http://stackoverflow.com/questions/3934331/android-how-to-encrypt-a-string – Sören Jan 31 '11 at 00:10
  • @SLaks: Thanks SHA512 is a bit of an overkill at this point in my development. – Squonk Jan 31 '11 at 00:30
  • @Soren: I had tried looking for previous posts but that one didn't come up. – Squonk Jan 31 '11 at 00:31
  • 1
    Why? SHA512 is no harder than MD5. You don't want to be stuck five years from now with legacy clients using MD5. – SLaks Jan 31 '11 at 00:31
  • 2
    I hope you are using a nonce in your protocol, so you can throw away replay attacks. – sarnold Jan 31 '11 at 00:31
  • @SLaks: More complex encryption is on the 'todo' list but I already have an MD5 hash method in the 'server' code for another reason (localhost comms to another server/service beyond my control). The project is at an 'alpha' testing stage and I'm not sure it'll still be in use in 5 months never mind 5 years. – Squonk Jan 31 '11 at 00:54
  • There's _no_ reason to use MD5 in non-legacy code. Every crypto library that provides MD5 also provides newer hashes, like SHA-1; why would you deliberately select the weaker option? – Nick Johnson Jun 14 '12 at 03:30
  • 1
    @NickJohnson : To answer your question **why would you deliberately select the weaker option?** with another question...why would you feel the need to comment on a question I posted 16 months ago? But if you really want to know (if you look at the comment to SLaks above yours), it was alpha stage code and the PC end (not written by me) used MD5 hashing. The requirement was basically for a pass-through scenario without involving extra complexity. I had about 10 alpha-stage testers at the time who knew the risks. More complex security has been incorporated since I asked the question. – Squonk Jun 14 '12 at 06:49
  • Sorry, SO's RSS feeds have been changed lately to pop questions to the top if they're edited or there's other activity on them, and I sometimes forget to check the date before commenting. – Nick Johnson Jun 14 '12 at 07:23
  • @NickJohnson : No problem. I agree the whole MD5 aspect was shaky to say the least. When I posted the question I was a rookie Android/Java developer just looking for some general pointers to get a "proof of concept" off the ground. It's a niche app and the security risks are minimal and, as I said, things have been tightened since then. – Squonk Jun 14 '12 at 09:44
  • @NickJohnson: Not necessarily: SHA-512 is said to be more secure than MD-5 because there are less collisions. However there are use cases where a less collisions mean less security: The theoretical security of salt-hashed password transmissions, for example! In such cases the security of hash algorithms **really** is: MD5 (strongest) > SHA-1 > SHA-256 > SHA-512 (weakest). – Martin Rosenau May 21 '17 at 13:47
  • 1
    ...what? No, that's not just wrong, it's dangerously wrong. – Nick Johnson May 22 '17 at 14:46

16 Answers16

243

Here is an implementation you can use (updated to use more up to date Java conventions - for:each loop, StringBuilder instead of 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 "";
}

Although it is not recommended for systems that involve even the basic level of security (MD5 is considered broken and can be easily exploited), it is sometimes enough for basic tasks.

GreenROBO
  • 4,725
  • 4
  • 23
  • 43
Den
  • 16,686
  • 4
  • 47
  • 87
  • Thanks that will do the trick. See my edit for why MD5 will suffice at this stage. – Squonk Jan 31 '11 at 00:29
  • 9
    Vote this down IMO so the more correct answers below will be surfaced. – Adam Aug 15 '12 at 23:55
  • 4
    0's not catered, as answered below. – SohailAziz Dec 19 '12 at 16:54
  • Updated to use newer java standards (for:each, StringBuilder) – loeschg Jan 27 '14 at 20:44
  • 3
    Are there any android operating system which have not MD5 implemented (cause throwing of `NoSuchAlgorithmException`)? – VSB Aug 22 '16 at 10:02
  • Tiny optimization: you can replace the `while` loop which is padding the value with zeroes with this `if (h.length() < 2) hexString.append("0");`. There will be at least one hexa digit, no need to cater for an empty string. – racs Aug 07 '18 at 02:36
  • You shouldn't use this as it remove some "0", Andranik answere work for me – Bastien May 25 '21 at 09:55
56

The accepted answer didn't work for me in Android 2.2. I don't know why, but it was "eating" some of my zeros (0) . Apache commons also didn't work on Android 2.2, because it uses methods that are supported only starting from Android 2.3.x. Also, if you want to just MD5 a string, Apache commons is too complex for that. Why one should keep a whole library to use just a small function from it...

Finally I found the following code snippet here which worked perfectly for me. I hope it will be useful for someone...

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;
}
AggelosK
  • 4,313
  • 2
  • 32
  • 37
Andranik
  • 2,729
  • 1
  • 29
  • 45
  • 5
    Worked for me. I've changed **md5.getBytes("UTF-8")**. I've checked it with: **q4m'x68n6_YDB4ty8VC4&}wqBtn^68W** with the above code, the result is **0c70bb931f03b75af1591f261eb77d0b**, NOT the *c70bb931f03b75af1591f261eb77d0b*. 0 is in place – Inoy Apr 13 '16 at 14:21
  • Also good to know that if you run on the emulator, you could use AdRequest.DEVICE_ID_EMULATOR, and that you can also check the ID via the logs (as mentioned here: https://www.howtodroid.com/how-to-find-hashed-device-id-for-admob.html ) by filtering by "addTestDevice" in the logs – android developer Aug 21 '23 at 23:21
31

The androidsnippets.com code does not work reliably because 0's seem to be cut out of the resulting hash.

A better implementation is here.

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;
}
Sufian
  • 6,405
  • 16
  • 66
  • 120
Christian
  • 327
  • 3
  • 2
  • Can "m" ever be null on Android, in this case? – android developer Nov 06 '16 at 15:04
  • 3
    @androiddeveloper You're totally right. The catch block is poorly implemented here, there needs to be a return statement or there will be a null reference error when m.update is called. Also I'm not sure but whereas this may not strip 0's on the individual bytes, I think it may still strip leading 0's on the entire 32 char hash. Instead of toString(16) I think String.Format("%032X", bigInt) works as intended. Plus you can choose whether you want the hex in upper or lower case ("%032x" for lower). – rsimp Jan 18 '17 at 16:37
  • 1
    This version is flawed. If your MD5 starts with "0", the generated MD5 will not have a leading 0. Please don't use this solution. – elcuco Jul 07 '19 at 07:53
22

If using Apache Commons Codec is an option, then this would be a shorter implementation:

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

Or SHA:

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

Source for above.

Please follow the link and upvote his solution to award the correct person.


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

Current Maven dependency (as of 6 July 2016):

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
Community
  • 1
  • 1
tbraun
  • 2,636
  • 31
  • 26
  • 1
    Any reason why would you use an external library for an API that already exists in the standard library? – m0skit0 Dec 19 '17 at 12:48
  • This is the only answer that could use without feeling like I'm in the 90's ;-) A nice one-liner, while other solutions look like hacks. – Pat Lee Feb 21 '21 at 12:41
12

A solution above using DigestUtils didn't work for me. In my version of Apache commons (the latest one for 2013) there is no such class.

I found another solution here in one blog. It works perfect and doesn't need Apache commons. It looks a little shorter than the code in accepted answer above.

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

You will need these imports:

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Denis Kutlubaev
  • 15,320
  • 6
  • 84
  • 70
  • Important to know id the MD5 hash is 32 char length or less, thank you! – Pelanes Nov 06 '14 at 09:45
  • 3
    The last few lines can probably just be replaced with: return String.Format("%032X", number); Otherwise I really like this answer. – rsimp Jan 18 '17 at 16:43
10

This is a slight variation of Andranik and Den Delimarsky answers above, but its a bit more concise and doesn't require any bitwise logic. Instead it uses the built-in String.format method to convert the bytes to two character hexadecimal strings (doesn't strip 0's). Normally I would just comment on their answers, but I don't have the reputation to do so.

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

If you'd like to return a lower case string instead, then just change %02X to %02x.

Edit: Using BigInteger like with wzbozon's answer, you can make the answer even more concise:

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
  • 791
  • 9
  • 9
5

Here is Kotlin version from @Andranik answer. We need to change getBytes to toByteArray (don't need to add charset UTF-8 because the default charset of toByteArray is UTF-8) and cast array[i] to 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
}

Hope it help

Linh
  • 57,942
  • 23
  • 262
  • 279
4

I have made a simple Library in Kotlin.

Add at Root build.gradle

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

at App build.gradle

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

Usage

In Kotlin

val ob = Hasher()

Then Use hash() method

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

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

It will return MD5 and SHA-1 Respectively.

More about the Library

https://github.com/ihimanshurawat/Hasher

Himanshu Rawat
  • 658
  • 5
  • 13
2

Please use SHA-512, MD5 is insecure

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

Useful Kotlin Extension Function Example

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

fun ByteArray.toHex(): String {
    return joinToString("") { "%02x".format(it) }
}
Arda Kazancı
  • 8,341
  • 4
  • 28
  • 50
1

In our MVC application we generate for long param

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

and same in Android application (thenk helps 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

i have used below method to give me md5 by passing string for which you want to get 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 "";
}
Squonk
  • 48,735
  • 19
  • 103
  • 135
0

MD5 is a bit old, SHA-1 is a better algorithm, there is a example here.

(Also as they note in that post, Java handles this on it's own, no Android specific code.)

Community
  • 1
  • 1
Adam
  • 25,966
  • 23
  • 76
  • 87
  • 4
    No - I didn't want alternatives to MD5 when I asked this question in January 2011 (19 months ago) and I'm not sure why you felt the need to respond to my question at this point. – Squonk Aug 16 '12 at 00:45
  • 21
    @Squonk I responded because that is the general idea behind stackoverflow. No matter how long after the fact, always try to get better answers for people who may come across the question later. As for suggesting SHA-1, there was no way for me to know you were specifically against SHA-1, but many others won't be, so again, it may help other people in the future that come across this, and direct them to a more modern algorithm. – Adam Aug 20 '12 at 20:57
  • 3
    I can't think of a single situation where MD5 is a bad choice but SHA1 a good one. If you need collision resistance, you need SHA2 not SHA1. If you hash passwords, you need bcrypt or PBKDF2. Or in the OP's case the proper solution is probably SSL. – CodesInChaos Aug 04 '14 at 12:27
  • 3
    @Adam this is not an answer this is a comment. – Antzi Aug 04 '16 at 09:42
0

Far too wasteful toHex() conversion prevails in other suggestions, really.

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
  • 736
  • 5
  • 14
  • The answer is about a "better" toHex when the quest is about MD5, as such it is non-responsive. Note: Donald Knuth *The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.* – zaph Mar 25 '16 at 12:58
0

this is working perfectly for me, I used this to get MD5 on LIST Array(then convert it to JSON object), but if you only need to apply it on your data. type format, replace JsonObject with yours.

Especially if you have a mismatch with python MD5 implementation use this!

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

The provided solutions for the Scala language (a little shorter):

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
  • 6,090
  • 4
  • 34
  • 57