Как получить соль из LDAP?

В организации, в которой я работаю, используется PPolicy (модуль OpenLDAP) для автоматического солевого и хеш-паролей. К сожалению, у меня нет доступа к машине, на которой запущен сервер OpenLDAP, поэтому я не могу посмотреть на файл конфигурации. Из того, что я видел, почти все, кажется, настраивается с использованием настроек по умолчанию.

Я хотел бы получить соль для конкретного пользователя. Если я смотрю на пользовательские атрибуты, userPassword является паролем SSHA. Я ничего не вижу о соли для этого конкретного пользователя. Я закончил поиск схемы LDAP, и я ничего не вижу о сотах.

Если бы вы догадались, где соль хранится для каждого пользователя, где это будет? Я понимаю, что это неопределенное и, вероятно, не много информации, но я не могу найти нигде в документах OpenLDAP, которые объясняют, где хранятся уникальные соли. Возможно, кто-то, кто настроил сервер OpenLDAP раньше, будет знать, где находится местоположение по умолчанию.

Спасибо.

Ответ 1

С SSHA обычно соль добавляется к хэшу SHA1, а затем все кодируется Base64 (я никогда не видел LDAP, который не делал SSHA таким образом). Вы должны быть в состоянии сказать это, посмотрев атрибут userPassword. Если он длиной 28 символов с a = в конце, это только хэш.

Если значение Base64 равно 32 символам в длину или больше, оно содержит как хэш, так и соль. Base64 декодирует значение и удаляет первые 20 байтов, это хэш SHA1. Остальные байты являются солью.

Пример:

                     Base64 encoded hash with salt
userPassword: {SSHA}MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0

Base64 decoded value
     SHA1 Hash      Salt
--------------------++++
123456789012345678901234

Edit: после двойной проверки кажется, что соли переменной длины иногда поддерживаются. Исправлено описание кодировки для учетной записи.

Ответ 2

Должность Syon мне очень помогла, спасибо за это! Я думал, что рабочий тест будет приятным дополнением для кого-то, кто борется с этой темой;).

public class SshaPasswordVerifyTest {
    private final static int SIZE_SHA1_HASH = 20;

    @Test
    public void itShouldVerifyPassword() throws Exception{
        String password = "YouNeverGuess!";
        String encodedPasswordWithSSHA = "{SSHA}M6HeeJAbwUCzuLwXbq00Fc3n3XcxFI8KjQkqeg==";
        Assert.assertEquals(encodedPasswordWithSSHA, getSshaDigestFor(password, getSalt(encodedPasswordWithSSHA)));
    }

    // The salt is the remaining part after the SHA1_hash
    private byte[] getSalt(String encodedPasswordWithSSHA){
        byte[] data = Base64.getMimeDecoder().decode(encodedPasswordWithSSHA.substring(6));
        return Arrays.copyOfRange(data, SIZE_SHA1_HASH, data.length);
    }

    private String getSshaDigestFor(String password, byte[] salt) throws Exception{
        // create a SHA1 digest of the password + salt
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(password.getBytes(Charset.forName("UTF-8")));
        crypt.update(salt);
        byte[] hash = crypt.digest();

        // concatenate the hash with the salt
        byte[] hashPlusSalt = new byte[hash.length + salt.length];
        System.arraycopy(hash, 0, hashPlusSalt, 0, hash.length);
        System.arraycopy(salt, 0, hashPlusSalt, hash.length, salt.length);

        // prepend the SSHA tag + base64 encode the result
        return "{SSHA}" + Base64.getEncoder().encodeToString(hashPlusSalt);
    }
}

Ответ 3

В PHP это сравнивает обычный текстовый пароль (обычно вводимый пользователем) с заданным хэш-кодом ssha (обычно хранящимся в вашем db):

private function checkSshaPassword($encrypted_password, $password)
{
    //  get hash and salt from encrypted_password
    $base_64_hash_with_salt = substr($encrypted_password, 6);
    $hash_with_salt = base64_decode($base_64_hash_with_salt);
    $hash = substr($hash_with_salt, 0, 20);
    $salt = substr($hash_with_salt, 20);

    //  hash given password
    $hash_given = sha1($password . $salt, true);

    return ($hash == $hash_given);
}