ASP.NET Core 3.1 - 2FA Authenticating

This article will describe the implementation of functions to generate 2FA keys and verify the code provided by the authenticator app. I will assume you have downloaded the ASP.NET Core 3.1 - Users Without Identity Project or created a new ASP.NET Core 3.1 Razor Pages project. See Tutorial: Get started with Razor Pages in ASP.NET Core. You should review the earlier articles of the Users Without Identity Project series.

Users Without Identity Project and Article Series

I developed a TwoFactorAuth class with functions to generate an authenticator key and verify the code provided by the authenticator app. The authenticator key must use base 32 characters. TwoFactorAuth.cs depends on the Identity Base32 class. See GitHub - Identity/ src/ Core/ Base32.cs

Authenticator apps generate a time-based one-time password (TOTP). The TOTP is a 6 digit code which is a hash of the key and the current time. To verify the TOTP you need to generate a code with the same algorithm and compare.

Services > TwoFactorAuth.cs:
public static class TwoFactorAuth
{
    public static string GetAuthenticatorKey()
    {
        // Generates a new base32 encoded 160-bit security secret (size of SHA1 hash).
        byte[] bytes = new byte[20];
        using (var rng = RandomNumberGenerator.Create())
            rng.GetBytes(bytes);
        return Base32.ToBase32(bytes);
    }

    public static int GetAuthenticatorCode(string key)
    {
        var unixTimestamp = (DateTime.UtcNow.Ticks - 621355968000000000L) / 10000000L;
        var window = unixTimestamp / (long)30;
        var keyBytes = Base32.FromBase32(key);
        var counter = BitConverter.GetBytes(window);
        if (BitConverter.IsLittleEndian)
            Array.Reverse(counter);

        var hmac = new HMACSHA1(keyBytes);
        var hash = hmac.ComputeHash(counter);
        var offset = hash[^1] & 0xf;

        // Convert the 4 bytes into an integer, ignoring the sign.
        var binary =
            ((hash[offset] & 0x7f) << 24)
            | (hash[offset + 1] << 16)
            | (hash[offset + 2] << 8)
            | (hash[offset + 3]);

        return binary % (int)Math.Pow(10, 6);
    }

    public static long GetCurrentCounter()
    {
        DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        return 30 - (long)(DateTime.UtcNow - unixEpoch).TotalSeconds % 30;
    }
}

I developed a demo which simulates an authenticator app. It allows you to scan an anonymous key into your app and compare verification codes and time remaining. See: Ken's Demo Site - Authenticator App

Authenticator App Demo.
Update 02/23/2021

I added the Enhanced User Series' article links.

Ken Haggerty
Created 08/20/20
Updated 02/24/21 00:01 GMT

Log In or Reset Quota to read more.

Article Tags:

2FA Authorization
Successfully completed. Thank you for contributing.
Processing...
Something went wrong. Please try again.
Contribute to enjoy content without advertisments.
You can contribute without registering.

Comments(0)

Loading...
Loading...

Not accepting new comments.

Submit your comment. Comments are moderated.

User Image.
DisplayedName - Member Since ?