Java - PBKDF2 с HMACSHA256 в качестве PRF - PullRequest
12 голосов
/ 05 февраля 2012

Мне дали задание создать API входа в систему для нашего проекта, и я должен использовать PBKDF2 с HMACSHA256 в качестве PRF.Простой текстовый пароль хэшируется с использованием MD5, а затем подается в PBKDF2 для генерации производного ключа.Проблема в том, что я не могу получить тот же вывод, что и в документации по проекту.

Вот реализация PBKDF2 в Java:

public class PBKDF2
{
    public static byte[] deriveKey( byte[] password, byte[] salt, int iterationCount, int dkLen )
        throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException
    {
        SecretKeySpec keyspec = new SecretKeySpec( password, "HmacSHA256" );
        Mac prf = Mac.getInstance( "HmacSHA256" );
        prf.init( keyspec );

        // Note: hLen, dkLen, l, r, T, F, etc. are horrible names for
        //       variables and functions in this day and age, but they
        //       reflect the terse symbols used in RFC 2898 to describe
        //       the PBKDF2 algorithm, which improves validation of the
        //       code vs. the RFC.
        //
        // dklen is expressed in bytes. (16 for a 128-bit key)

        int hLen = prf.getMacLength();   // 20 for SHA1
        int l = Math.max( dkLen, hLen); //  1 for 128bit (16-byte) keys
        int r = dkLen - (l-1)*hLen;      // 16 for 128bit (16-byte) keys
        byte T[] = new byte[l * hLen];
        int ti_offset = 0;
        for (int i = 1; i <= l; i++) {
            F( T, ti_offset, prf, salt, iterationCount, i );
            ti_offset += hLen;
        }

        if (r < hLen) {
            // Incomplete last block
            byte DK[] = new byte[dkLen];
            System.arraycopy(T, 0, DK, 0, dkLen);
            return DK;
        }
        return T;
    } 


    private static void F( byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex ) {
        final int hLen = prf.getMacLength();
        byte U_r[] = new byte[ hLen ];
        // U0 = S || INT (i);
        byte U_i[] = new byte[S.length + 4];
        System.arraycopy( S, 0, U_i, 0, S.length );
        INT( U_i, S.length, blockIndex );
        for( int i = 0; i < c; i++ ) {
            U_i = prf.doFinal( U_i );
            xor( U_r, U_i );
        }

        System.arraycopy( U_r, 0, dest, offset, hLen );
    }

    private static void xor( byte[] dest, byte[] src ) {
        for( int i = 0; i < dest.length; i++ ) {
            dest[i] ^= src[i];
        }
    }

    private static void INT( byte[] dest, int offset, int i ) {
        dest[offset + 0] = (byte) (i / (256 * 256 * 256));
        dest[offset + 1] = (byte) (i / (256 * 256));
        dest[offset + 2] = (byte) (i / (256));
        dest[offset + 3] = (byte) (i);
    } 

    // ctor
    private PBKDF2 () {}

}

Я использовал найденные тестовые векторыздесь векторы испытаний PBKDF2-HMAC-SHA2 , чтобы проверить правильность реализации и все это проверено.Я не уверен, почему не смог получить те же результаты с хешированным паролем MD5.

Параметры:

Salt: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F
Iterations Count: 1000
DKLen: 16 (128-bit derived key)

При использовании «foobar» в качестве пароля в виде открытого текста ожидаемые результаты:

PWHash = MD5(PlaintextPassword) = 3858f62230ac3c915f300c664312c63f
PWKey = PBKDF2(PWHash, Salt, IterationsCount, DKLen) = 33C37758EFA6780C5E52FAB3B50F329C

Что я получаю:

PWHash = 3858f62230ac3c915f300c664312c63f
PWKey = 0bd0c7d8339df2c66ce4b6e1e91ed3f1

Ответы [ 2 ]

3 голосов
/ 06 февраля 2012

Количество итераций должно было составлять 4096, а не 1000.

0 голосов
/ 24 июня 2018

Поколение int l кажется неправильным.Вы указали максимум между dkLen и hLen, но спецификация гласит: l = CEIL (dkLen / hLen) с

CEIL (x) - это функция "потолка", т.е. наименьшее целое число больше или равнок, х.

Я думаю, l будет более точно определено как l = (int)Math.ceil( (double)dkLen / (double)hLen )

...