MessageDigest хэши по-разному на разных машинах

У меня возникла проблема с MessageDigest, возвращающим разные значения хэша на разных компьютерах.

На одном компьютере работает 32-разрядная Java на Windows Vista, а на другой - 64-разрядная Java на Mac OS. Я не уверен, что это связано с тем, что MessageDigest зависит от машины, или мне нужно явно указать кодировку символов где-нибудь или, возможно, что-то еще. Здесь код:

public static boolean authenticate(String salt, String encryptedPassword, 
    char[] plainTextPassword ) throws NoSuchAlgorithmException {

    // do I need to explcitly specify character encoding here? -->
    String saltPlusPlainTextPassword = salt + new String(plainTextPassword);

    MessageDigest sha = MessageDigest.getInstance("SHA-512");

    // is this machine dependent? -->
    sha.update(saltPlusPlainTextPassword.getBytes());
    byte[] hashedByteArray = sha.digest();

    // or... perhaps theres a translation problem here? -->
    String hashed = new String(hashedByteArray);

    return hashed.equals(encryptedPassword);
}

Должен ли этот код работать по-разному на этих двух разных машинах? Если он зависит от машины, как я его написал, есть ли другой способ хешировать эти пароли, которые более переносимы? Спасибо!

Изменить:::

Это код, который я использую для создания солей:

public static String getSalt() {
   int size = 16;
   byte[] bytes = new byte[size];
   new Random().nextBytes(bytes);
   return org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(bytes);
}

Решение:

Благодаря принятому решению я смог исправить свой код:

public static boolean authenticate_(String salt, String encryptedPassword, 
        char[] plainTextPassword ) throws NoSuchAlgorithmException, UnsupportedEncodingException {

        // This was ok
        String saltPlusPlainTextPassword = salt + new String(plainTextPassword);    

        MessageDigest sha = MessageDigest.getInstance("SHA-512");

        // must specify "UTF-8" encoding
        sha.update(saltPlusPlainTextPassword.getBytes("UTF-8"));
        byte[] hashedByteArray = sha.digest();

        // Use Base64 encoding here -->
        String hashed = org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(hashedByteArray);

        return hashed.equals(encryptedPassword);
    }

Ответ 1

Кодировки вызывают проблемы. Сначала здесь:

saltPlusPlainTextPassword.getBytes()

Это будет использовать кодировку по умолчанию для машины. Плохая идея. Укажите "UTF-8" как простое решение. (Он гарантированно присутствует.)

Далее возникают проблемы:

String hashed = new String(hashedByteArray);

hashedByteArray - произвольные двоичные данные. Чтобы безопасно преобразовать его в текст, используйте кодировку base-64 или просто hex. Опять же, вы используете стандартную кодировку, которая будет отличаться от машины к машине. В Java есть множество сторонних библиотек для кодировки base64.

Ответ 2

Вероятно, решение Jon Skeet, приведенное выше, является причиной, и его рекомендации обязательно должны быть приняты во внимание, но другая возможная причина - непонимание соли.

Соль представляет собой полусекретное случайное значение, которое применяется к строкам до хэширования. Это затрудняет выполнение атаки с использованием грубой силы, когда вы пытаетесь угадать, что такое исходная строка, потому что соль, по-видимому, неизвестна злоумышленнику.

Значения солей обычно различаются при установке. Возможно, что фактическая причина заключается в том, что у вас есть значения соли, установленные по-разному на разных машинах.