HMAC на основе одноразового пароля в C # (RFC 4226 - HotP)
-
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 цифр, предложенных в вышеуказанных алгоритмах.
Решение
У вас есть две проблемы здесь:
Если вы генерируете Alpha-Numeric, вы не соответствуете RFC - на данный момент вы можете просто взять любые N BYTES и превратить их в шестигранную строку и получить альфа-числовую. Или, Преобразуйте их в базу 36 Если вы хотите AZ и 0-9. Раздел 5.4 RFC дает вам стандартную HOTP Calc для набора
Digit
параметр (обратите внимание, чтоDigit
это параметр вместе сC
,K
, иT
). Если вы решите игнорировать этот раздел, вам не нужно преобразовывать код - просто используйте то, что вы хотите.Ваш «результат» байтового массива имеет время истечения, просто набившись первыми 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();
}
}