كلمة مرور لمرة واحدة تستند إلى 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

ثم يتم تقديم مثال على 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 أرقام كما هو مقترح في الخوارزميات أعلاه.

هل كانت مفيدة؟

المحلول

لديك قضيتان هنا:

  1. إذا كنت تقوم بإنشاء ألفا رقمي ، فأنت لا تتوافق مع RFC-في هذه المرحلة ، يمكنك ببساطة أخذ أي بايت وتحويلها إلى سلسلة سداسية والحصول على alphaeric. أو، تحويلها إلى قاعدة 36 إذا كنت تريد AZ و 0-9. يمنحك القسم 5.4 من RFC Cals HotP القياسي لمجموعة Digit المعلمة (لاحظ ذلك Digit هو معلمة جنبا إلى جنب مع C, K, ، و T). إذا كنت تختار تجاهل هذا القسم ، فلن تحتاج إلى تحويل الرمز - فقط استخدم ما تريد.

  2. تتمتع مجموعة "النتيجة" الخاصة بك بوقت انتهاء الصلاحية ببساطة محشوة في أول 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();
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top