MD5 and SHA256 in Java Kotlin and Android

Upasana | July 26, 2020 | 4 min read | 10,824 views


In this article we will discuss different options to compute MD5 (or theoretically any other Hash Function such as SHA-1, SHA-256) using Java, Android and Kotlin.

MD5 is widely used hash function (cryptographically weak) that produces 128 bit hash value. It is mostly used as a checksum to verify data integrity, but only against unintentional corruption. It is not used for security purpose anymore because it suffer from extensive vulnerabilities(Collision and Preimage vulnerabilities specifically).

1. Java Standard Library implementation

Java Standard Library has MessageDigest class which provides applications the functionality of a message digest algorithm, such as MD5, SHA-1 or SHA-256. Message digests are secure one-way hash functions that take arbitrary-sized data and output a fixed-length hash value.

Using Java Standard Library
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

class Utils {

    public String md5(String input) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(input.getBytes());
        byte[] digest = md.digest();    (1)
        return DatatypeConverter.printHexBinary(digest).toUpperCase();  (2)
    }
}
1 Computes Message Digest Code in byte array format
2 Converts byte array to base 16 Hex encoded String
Kotlin variant for the same
import java.security.MessageDigest
import javax.xml.bind.DatatypeConverter

object Utils {

    fun sha1(input: String) = hashString("SHA-1", input)
    fun md5(input: String) = hashString("MD5", input)

    private fun hashString(type: String, input: String): String {
        val bytes = MessageDigest
                .getInstance(type)
                .digest(input.toByteArray())
        return DatatypeConverter.printHexBinary(bytes).toUpperCase()
    }
}

The above class can be used to compute any type of hash supported by underlying JVM implementation like, SHA-1, SHA-256, MD5, etc.

This implementation requires DatatypeConverter for encoding byte array into Hex Encoded String, which is only available with JDK, so this approach cannot be used in Android.

Alternatives for Hex Encoding

There are many ways we can convert a given byte array into Hex Encoded string. These methods mainly differ in performance and external dependencies.

a) Using String.format

The underlying algorithm is same but bytes to hexadecimal conversion in this case is done using String.format function instead of DatatypeConverter.

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

fun ByteArray.toHex(): String {
    return joinToString("") { "%02x".format(it) }   (2)
}
1 This calls the extension function created on ByteArray
2 02x instructs String format function to print each byte in Hexadecimal format making sure output is padded with 0 in-case of insufficient characters.

Now we can use the below syntax to calculate md5 on any string:

val hash = "foo-bar".toMD5()
Reference

b) Using BigInteger

BigInteger allows us to convert a given number to Base16 format using toString(16) member function.

We can write extension functions in Kotlin that adds md5() as the function to String class.

Writing extension function in kotlin
import java.math.BigInteger
import java.security.MessageDigest

fun String.md5(): String {
    val md = MessageDigest.getInstance("MD5")
    return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
}

fun String.sha256(): String {
    val md = MessageDigest.getInstance("SHA-256")
    return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
}

Now we can call this extension function to compute md5/sha-256 on any arbitrary string value.

Using extension function
val input = "this is a dummy string"
val md5 = input.md5()
println("computed md5 value is $md5")

val sha256 = input.sha256()
println("computed sha256 value is $sha256")

c) Android Kotlin implementation

DatatypeConverter class is not available in Android, so we can use the below function to convert a given byteArray to hexadecimal format in Android.

const val HEX_CHARS = "0123456789ABCDEF".toCharArray()

fun printHexBinary(data: ByteArray): String {
    val r = StringBuilder(data.size * 2)
    data.forEach { b ->
        val i = b.toInt()
        r.append(HEX_CHARS[i shr 4 and 0xF])
        r.append(HEX_CHARS[i and 0xF])
    }
    return r.toString()
}

Performance of this method is better than BigInteger and String.format approach and is quite comparable to Apache Commons Codec implementation. Wherever possible, this approach can be used unless you are already using other libraries in your project (Apache Commons Codec, Google Guava)

2. Apache Commons Codec

We can use Apache Common Codec to calculate MD5 Hash value in single line of code. First of all we need to add the below dependency to our build.gradle file.

build.gradle
// https://mvnrepository.com/artifact/commons-codec/commons-codec
compile group: 'commons-codec', name: 'commons-codec', version: '1.13'

If you are using maven, then do it this way:

pom.xml
<!-- https://search.maven.org/remotecontent?filepath=commons-codec/commons-codec/1.13/commons-codec-1.12.jar -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.13</version>
</dependency>

Now, we can use DigestUtils to calculate md5 hash for any string.

DigestUtils
import org.apache.commons.codec.digest.DigestUtils;

public class Util {

    public static String md5() {
        return DigestUtils.md5Hex("foo bar").toUpperCase();
    }
}

3. Google guava Hashing function

Google guava can also be used to calculate checksums and SHA.

First of all get the latest version of guava library in build.gradle

build.gradle
dependencies {
    compile group: 'com.google.guava', name: 'guava', version: '29.0-jre'
}

Now we can use Hashing class provided by Guava for calculating various hash values (md5, sha1, sha256, etc.)

SHA-256 using Guava
import com.google.common.base.Charsets;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;

public class GuavaTest {

    public void testHashingSha256() {
        final HashFunction hashFunction = Hashing.sha256();
        final HashCode hc = hashFunction
                .newHasher()
                .putString("The quick brown fox jumps over the lazy dog", Charsets.UTF_8)
                .hash();
        final String sha256 = hc.toString();
        System.out.println("sha256 = " + sha256);
    }
}
output
sha256 = d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592

That’s all.


Top articles in this category:
  1. Sapient Global Market Java Interview Questions and Coding Exercise
  2. Java Concurrency Interview Questions
  3. Goldman Sachs Java Interview Questions
  4. Multi-threading Java Interview Questions for Investment Bank
  5. What is difference between Primary key and Unique Key
  6. RBS Java Programming Interview Questions
  7. Mandrill emails in Spring Boot Java

Recommended books for interview preparation:

Find more on this topic:
Buy interview books

Java & Microservices interview refresher for experienced developers.