Reinherd Reinherd - 3 months ago 9
Android Question

Which hashing algorithms are available in Android?

This question might sound a little bit stupid, but there's a lack of documentation.

In order to hash strings in Android, we can use

MessageDigest
, which is from java.security package.

However, the basic setup is like the following:

MessageDigest.getInstance( "SHA-512 );


Which is not cool, because:


  1. Can we know which algorithms are available on the current device? What does it depend on? Android SDK? Java SDK? This is painful in Android, because of the segmentation we've to handle...

  2. Why the hell don't we have a Constant / Enum for that string??? Aren't them common for the whole world?



I hope you can answer both of my answers.

Thanks.

Answer

As someone already mentioned, there's not a clear way to find out which algorithms are available. So what I decided to do, is to create a helper for that.

import android.util.Base64;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.KeySpec;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

/**
 * Created by sergi.castellsague on 04/05/2014.
 */
public class SecurityManager
{
private static final int ITERATIONS = 1000;

public enum HashMethod
{
    PBKDF2(){
        @Override
        public String getHashString()
        {
            return "PBKDF2WithHmacSHA1";
        }
    }, SHA512(){
        @Override
        public String getHashString() {
            return "SHA-512";
        }
    }, SHA384() {
        @Override
        public String getHashString() {
            return "SHA-384";
        }
    }, SHA256() {
        @Override
        public String getHashString () {
            return "SHA-256";
        }
    }
    , SHA1()
    {
        @Override
        public String getHashString() {
            return "SHA-1";
        }
    };

    public abstract String getHashString();

}

public static HashMethod getAppropriateHash()
{
    HashMethod method = null;

    if ( isPBKDFAvailable() )
    {
        method = HashMethod.PBKDF2;
    }
    else if( isDigestAvailable( HashMethod.SHA512.getHashString() ) )
    {
        method = HashMethod.SHA512;
    }
    else if( isDigestAvailable( HashMethod.SHA384.getHashString() ) )
    {
        method = HashMethod.SHA384;
    }
    else if( isDigestAvailable( HashMethod.SHA256.getHashString() ) )
    {
        method = HashMethod.SHA256;
    }
    else if( isDigestAvailable( HashMethod.SHA1.getHashString() ) )
    {
        method = HashMethod.SHA1;
    }

    return method;
}


private static boolean isPBKDFAvailable()
{
    try
    {
        SecretKeyFactory.getInstance( HashMethod.PBKDF2.getHashString() );
    }
    catch ( Exception notAvailable)
    {
        return false;
    }
    return true;
}

private static boolean isDigestAvailable( String method )
{
    try
    {
        MessageDigest.getInstance( method );
    }
    catch ( Exception notAvailable )
    {
        return false;
    }

    return true;
}

public static String getHashedPassword( HashMethod method, String password )
{
    String hashed;

    if ( HashMethod.PBKDF2.getHashString().equals( method.getHashString() ) )
    {
        hashed = generatePBKDF( password );
    }
    else
    {
        hashed = password;
        for ( int i = 0; i < ITERATIONS; i++ )
        {
            hashed = generateDigestPassword( password, method.getHashString() );
        }
    }

    return hashed;
}

private static String generatePBKDF( String password )
{
    // Generate a 512-bit key
    final int outputKeyLength = 512;

    char[] chars = new char[password.length()];
    password.getChars( 0, password.length(), chars, 0 );
    byte[] salt = "salt_on_client_is_funny".getBytes(); // In security terms, this is worthess. However, it's required.

    byte[] hashedPassBytes = new byte[0];
    try
    {
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance( HashMethod.PBKDF2.getHashString() );
        KeySpec keySpec = new PBEKeySpec( chars, salt, ITERATIONS, outputKeyLength );

        hashedPassBytes = secretKeyFactory.generateSecret( keySpec ).getEncoded();
    }
    catch ( Exception shouldNotHappen )
    {}

    return Base64.encodeToString( hashedPassBytes, Base64.DEFAULT );
}

private static String generateDigestPassword( String password, String algorithm )
{
    byte[] digest = new byte[0];
    byte[] buffer = password.getBytes();

    try {
        MessageDigest messageDigest = MessageDigest.getInstance( algorithm );
        messageDigest.reset();
        messageDigest.update( buffer );
        digest = messageDigest.digest();
    }
    catch ( NoSuchAlgorithmException ex )
    {}

    return Base64.encodeToString(digest, Base64.DEFAULT);
}
}

The usage is pretty simple:

String password = "BestPasswordEver123!!";
SecurityManager.HashMethod hashMethod = SecurityManager.getAppropriateHash();
SecurityManager.getHashedPassword( hashMethod, password )

Oh, and note that depending on:

  1. Algorithm used
  2. Amount of iterations
  3. Device

The calculation, might be something from 0.5s to 10s (or more...), so you better do it in an other Thread =)

Comments