Question

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, therefore:

  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 questions.

Thanks.

Was it helpful?

Solution 2

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 =)

OTHER TIPS

https://developer.android.com/reference/java/security/MessageDigest.html shows the following:

Name    | Supported (API Levels)
MD5     | 1+
SHA-1   | 1+
SHA-224 | 1–8,22+
SHA-256 | 1+
SHA-384 | 1+
SHA-512 | 1+

Does this help?

I bumbed into this question while I was looking for hash function implementation. I know that this question has over 7 years but here is the link from the documentation about the avaible algorithms (with api levels):

https://developer.android.com/reference/javax/crypto/SecretKeyFactory

All I can think of is trial and error. Find out which values the hasher instantiator will accept, and to make sure it's the right hash compare the output with what you get from sha256sum or related commands.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top