كلمة مرور لمرة واحدة تستند إلى 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
ثم يتم تقديم مثال على HotP المكون من 6 أرقام
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 أرقام كما هو مقترح في الخوارزميات أعلاه.
المحلول
لديك قضيتان هنا:
إذا كنت تقوم بإنشاء ألفا رقمي ، فأنت لا تتوافق مع RFC-في هذه المرحلة ، يمكنك ببساطة أخذ أي بايت وتحويلها إلى سلسلة سداسية والحصول على alphaeric. أو، تحويلها إلى قاعدة 36 إذا كنت تريد AZ و 0-9. يمنحك القسم 5.4 من RFC Cals HotP القياسي لمجموعة
Digit
المعلمة (لاحظ ذلكDigit
هو معلمة جنبا إلى جنب معC
,K
, ، وT
). إذا كنت تختار تجاهل هذا القسم ، فلن تحتاج إلى تحويل الرمز - فقط استخدم ما تريد.تتمتع مجموعة "النتيجة" الخاصة بك بوقت انتهاء الصلاحية ببساطة محشوة في أول 8 بايت بعد التجزئة. إذا كان اقتطاعك إلى 6 أرقام أبجدية رقمية لا يجمعها مع أجزاء من التجزئة ، فقد لا يتم حسابه على الإطلاق. من السهل جدًا أيضًا "مزيفًا" أو إعادة تشغيل - Hash السر مرة واحدة ، ثم ضع كل ما تريده أمامه - وليس كلمة مرور لمرة واحدة حقًا. لاحظ أن المعلمة
C
في RFC يهدف إلى الوفاء بالنافذة الصالحة الصلاحية ويجب إضافتها إلى المدخلات قبل ذ لك حساب رمز التجزئة.
نصائح أخرى
لأي شخص مهتم ، اكتشفت طريقة لبناء انتهاء الصلاحية في كلمة المرور لمرة واحدة. هذا النهج هو استخدام الوقت الذي تم إنشاؤه وصولاً إلى الدقيقة (تجاهل الثواني ، ميلي ثانية ، إلخ). بمجرد حصولك على هذه القيمة ، استخدم علامات DateTime كجهاز عدادك ، أو متغير C.
otpLifespan
هل عمر HotP الخاص بي في دقائق.
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
يمتد HotP المنتهي من انتهاء الصلاحية من HotP الرقمي الذي يحتوي على طريقة التحقق من الصحة الثابتة التي تتحقق من الطول ، ويضمن أن يكون رقميًا ، ويتحقق من التحقق من الفحص إذا تم استخدامه ، وأخيراً يقارن HotP الذي تم تمريره باستخدام طريقة تم إنشاؤها.
الجانب السلبي الوحيد لذلك هو أنه في كل مرة تقوم فيها بالتحقق من صحة HotP المنتهية ، فإن سيناريو الحالة الأسوأ هو التحقق من قيم HOTP N + 1 حيث تكون N هي العمر في دقائق.
كان مثال رمز Java في المستند الذي يحدد 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();
}
}