HMAC на основе одноразового пароля в C # (RFC 4226 - HotP)

StackOverflow https://stackoverflow.com/questions/4308003

  •  29-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь обернуть мой мозг вокруг генерации 6-значного / символа, чувствительный к регистру срок действия одноразового пароля.

Мой источник есть http://tools.ietf.org/html/rfc4226#section-5.

Сначала определение параметров

C       8-byte counter value, the moving factor.  This counter
       MUST be synchronized between the HOTP generator (client)
       and the HOTP validator (server).

K       shared secret between client and server; each HOTP
       generator has a different and unique secret K.

T       throttling parameter: the server will refuse connections
       from a user after T unsuccessful authentication attempts.

Тогда у нас есть алгоритм генерировать HOTP

As the output of the HMAC-SHA-1 calculation is 160 bits, we must
   truncate this value to something that can be easily entered by a
   user.

                   HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

Затем мы урезали, определенные как

String = String[0]...String[19]
 Let OffsetBits be the low-order 4 bits of String[19]
 Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15
 Let P = String[OffSet]...String[OffSet+3]
 Return the Last 31 bits of P

А затем пример предлагается для 6-значной HOTP

The following code example describes the extraction of a dynamic
binary code given that hmac_result is a byte array with the HMAC-
SHA-1 result:

    int offset   =  hmac_result[19] & 0xf ;
    int bin_code = (hmac_result[offset]  & 0x7f) << 24
       | (hmac_result[offset+1] & 0xff) << 16
       | (hmac_result[offset+2] & 0xff) <<  8
       | (hmac_result[offset+3] & 0xff) ;

Я скорее в потере попытки преобразовать это в полезный код C # для генерации одноразовых паролей. У меня уже есть код для создания истекающего HMAC следующим образом:

byte[] hashBytes = alg.ComputeHash(Encoding.UTF8.GetBytes(input));
byte[] result = new byte[8 + hashBytes.Length];

hashBytes.CopyTo(result, 8);
BitConverter.GetBytes(expireDate.Ticks).CopyTo(result, 0);

Я просто не уверен, как пойти от этого, до 6 цифр, предложенных в вышеуказанных алгоритмах.

Это было полезно?

Решение

У вас есть две проблемы здесь:

  1. Если вы генерируете Alpha-Numeric, вы не соответствуете RFC - на данный момент вы можете просто взять любые N BYTES и превратить их в шестигранную строку и получить альфа-числовую. Или, Преобразуйте их в базу 36 Если вы хотите AZ и 0-9. Раздел 5.4 RFC дает вам стандартную HOTP Calc для набора Digit параметр (обратите внимание, что Digit это параметр вместе с C, K, и T). Если вы решите игнорировать этот раздел, вам не нужно преобразовывать код - просто используйте то, что вы хотите.

  2. Ваш «результат» байтового массива имеет время истечения, просто набившись первыми 8 байтами после перемешивания. Если ваше усечение до 6-значного буквенно-цифровой не собирает их вместе с частями хеша, он может также не быть рассчитан вообще. Также очень легко «подделка» или воспроизвести - хеш секретным один раз, затем поставь любые тики, которые вы хотите перед ним - не один раз пароль. Обратите внимание, что параметр C В RFC предназначен для выполнения окончающего окна и должно быть добавлено к входу до вычисляет хеш-код.

Другие советы

Для тех, кто заинтересован, я выяснил способ строить истечения срока действия моим одноразовым паролем. Подход состоит в том, чтобы использовать созданное время до минуты (игнорирующие секунды, миллисекунды и т. Д.). Как только у вас есть это значение, используйте галочки The DateTime в качестве счетчика или переменной C.

otpLifespan Является ли моя срок службы Hot P в минутах.

DateTime current = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 
    DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, 0);

for (int x = 0; x <= otpLifespan; x++)
{
    var result = NumericHOTP.Validate(hotp, key, 
        current.AddMinutes(-1 * x).Ticks);

    //return valid state if validation succeeded

    //return invalid state if the passed in value is invalid 
    //  (length, non-numeric, checksum invalid)
}

//return expired state

Моя срока действия Hot P распространяется на мою числовую HOTP, которая имеет статический метод проверки, который проверяет длину, гарантирует, что он является числовым, проверяет контрольную сумму, если оно используется, и, наконец, сравнивает, прошедший с генерируемым.

Единственным недостатком для этого является то, что каждый раз, когда вы устанавливаете истекю HOTP, ваш Scept Case Scenario должен проверить значения HOTP N + 1, где N - это продолжительность жизни в минутах.

Пример кода Java в документе Outlining RFC 4226 был очень простым движением в C #. Единственная часть, которую мне действительно пришлось приложить какие-либо усилия в перезаписи, был методом хеширования.

private static byte[] HashHMACSHA1(byte[] keyBytes, byte[] text)
{
    HMAC alg = new HMACSHA1(keyBytes);

    return alg.ComputeHash(text);
}

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

Этот фрагмент должен делать то, что вы просите:

  public class UniqueId
{
    public static string GetUniqueKey()
    {
        int maxSize = 6; // whatever length you want
        char[] chars = new char[62];
        string a;
        a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
           char[] chars = new char[a.Length];
        chars = a.ToCharArray();
        int size = maxSize;
        byte[] data = new byte[1];
        RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
        crypto.GetNonZeroBytes(data);
        size = maxSize;
        data = new byte[size];
        crypto.GetNonZeroBytes(data);
        StringBuilder result = new StringBuilder(size);
        foreach (byte b in data)
        { result.Append(chars[b % (chars.Length - 1)]); }
        return result.ToString();
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top