Какие алгоритмы хэширования доступны в Android?

этот вопрос может показаться немного глупым, но есть недостаток документации.

чтобы хэшировать строки в Android, мы можем использовать MessageDigest, который из java.пакет безопасности.

однако базовая настройка выглядит следующим образом:

MessageDigest.getInstance( "SHA-512" );

это не круто, поэтому:

  1. можем ли мы знать, какие алгоритмы доступны на текущем устройстве? От чего это зависит? Android SDK? Java SDK? Это больно в Android, из-за сегментации, с которой мы должны справиться...

  2. почему, черт возьми, у нас нет константы / перечисления для этой строки??? Разве они не обычны для всего мира?

Я надеюсь, что вы можете ответить на мои вопросы.

спасибо.

3 ответов


как кто-то уже упоминал, нет четкого способа узнать, какие алгоритмы доступны. Поэтому я решил создать помощника для этого.

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

использование довольно простое:

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

да, и обратите внимание, что в зависимости от:

    используется
  1. количество итераций
  2. устройства

расчет, может быть что-то от 0.5 до 10 (или больше...), так что тебе лучше сделайте это в другом потоке =)


https://developer.android.com/reference/java/security/MessageDigest.html показывает следующее:

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

это поможет?


Все, о чем я могу думать, это проб и ошибок. Узнайте, какие значения будет принимать hasher instantiator, и чтобы убедиться, что это правильный хэш, сравните вывод с тем, что вы получаете от sha256sum или связанных команд.