是否有关于如何在.NET应用程序中实现Google身份验证器的教程?

时间:2011-06-21 07:44:14

标签: .net google-authenticator two-factor-authentication

我正在寻找有关如何在.NET应用程序中使用Google Authenticator的教程。这是否存在,如果存在,我在哪里可以找到它?

据我所知,这可用于为您自己的应用添加双因素身份验证。

6 个答案:

答案 0 :(得分:38)

在玩Google身份验证器时,我遇到了这个问题,特别是Espo提供的代码。我个人对从Java到C#的转换不满意,所以我想我会分享我的版本。除了重构代码:

  • 引入了对little-endian字节排序的检查,并根据需要转换为big-endian。
  • 介绍了HMAC密钥的参数。

有关配置网址格式的详细信息,另请参阅:https://github.com/google/google-authenticator/wiki/Key-Uri-Format

如果您愿意,请随意使用,并感谢Espo的初步工作。

using System;
using System.Globalization;
using System.Net;
using System.Security.Cryptography;
using System.Text;

public class GoogleAuthenticator
{
    const int IntervalLength = 30;
    const int PinLength = 6;
    static readonly int PinModulo = (int)Math.Pow(10, PinLength);
    static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

    /// <summary>
    ///   Number of intervals that have elapsed.
    /// </summary>
    static long CurrentInterval
    {
        get
        {
            var ElapsedSeconds = (long)Math.Floor((DateTime.UtcNow - UnixEpoch).TotalSeconds);

            return ElapsedSeconds/IntervalLength;
        }
    }

    /// <summary>
    ///   Generates a QR code bitmap for provisioning.
    /// </summary>
    public byte[] GenerateProvisioningImage(string identifier, byte[] key, int width, int height)
    {
        var KeyString = Encoder.Base32Encode(key);
        var ProvisionUrl = Encoder.UrlEncode(string.Format("otpauth://totp/{0}?secret={1}&issuer=MyCompany", identifier, KeyString));

        var ChartUrl = string.Format("https://chart.apis.google.com/chart?cht=qr&chs={0}x{1}&chl={2}", width, height, ProvisionUrl);
        using (var Client = new WebClient())
        {
            return Client.DownloadData(ChartUrl);
        }
    }

    /// <summary>
    ///   Generates a pin for the given key.
    /// </summary>
    public string GeneratePin(byte[] key)
    {
        return GeneratePin(key, CurrentInterval);
    }

    /// <summary>
    ///   Generates a pin by hashing a key and counter.
    /// </summary>
    static string GeneratePin(byte[] key, long counter)
    {
        const int SizeOfInt32 = 4;

        var CounterBytes = BitConverter.GetBytes(counter);

        if (BitConverter.IsLittleEndian)
        {
            //spec requires bytes in big-endian order
            Array.Reverse(CounterBytes);
        }

        var Hash = new HMACSHA1(key).ComputeHash(CounterBytes);
        var Offset = Hash[Hash.Length - 1] & 0xF;

        var SelectedBytes = new byte[SizeOfInt32];
        Buffer.BlockCopy(Hash, Offset, SelectedBytes, 0, SizeOfInt32);

        if (BitConverter.IsLittleEndian)
        {
            //spec interprets bytes in big-endian order
            Array.Reverse(SelectedBytes);
        }

        var SelectedInteger = BitConverter.ToInt32(SelectedBytes, 0);

        //remove the most significant bit for interoperability per spec
        var TruncatedHash = SelectedInteger & 0x7FFFFFFF;

        //generate number of digits for given pin length
        var Pin = TruncatedHash%PinModulo;

        return Pin.ToString(CultureInfo.InvariantCulture).PadLeft(PinLength, '0');
    }

    #region Nested type: Encoder

    static class Encoder
    {
        /// <summary>
        ///   Url Encoding (with upper-case hexadecimal per OATH specification)
        /// </summary>
        public static string UrlEncode(string value)
        {
            const string UrlEncodeAlphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

            var Builder = new StringBuilder();

            for (var i = 0; i < value.Length; i++)
            {
                var Symbol = value[i];

                if (UrlEncodeAlphabet.IndexOf(Symbol) != -1)
                {
                    Builder.Append(Symbol);
                }
                else
                {
                    Builder.Append('%');
                    Builder.Append(((int)Symbol).ToString("X2"));
                }
            }

            return Builder.ToString();
        }

        /// <summary>
        ///   Base-32 Encoding
        /// </summary>
        public static string Base32Encode(byte[] data)
        {
            const int InByteSize = 8;
            const int OutByteSize = 5;
            const string Base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

            int i = 0, index = 0;
            var Builder = new StringBuilder((data.Length + 7)*InByteSize/OutByteSize);

            while (i < data.Length)
            {
                int CurrentByte = data[i];
                int Digit;

                //Is the current digit going to span a byte boundary?
                if (index > (InByteSize - OutByteSize))
                {
                    int NextByte;

                    if ((i + 1) < data.Length)
                    {
                        NextByte = data[i + 1];
                    }
                    else
                    {
                        NextByte = 0;
                    }

                    Digit = CurrentByte & (0xFF >> index);
                    index = (index + OutByteSize)%InByteSize;
                    Digit <<= index;
                    Digit |= NextByte >> (InByteSize - index);
                    i++;
                }
                else
                {
                    Digit = (CurrentByte >> (InByteSize - (index + OutByteSize))) & 0x1F;
                    index = (index + OutByteSize)%InByteSize;

                    if (index == 0)
                    {
                        i++;
                    }
                }

                Builder.Append(Base32Alphabet[Digit]);
            }

            return Builder.ToString();
        }
    }

    #endregion
}

答案 1 :(得分:11)

经过一些研究和测试,我创建了自己的“概念验证”,了解如何生成QR图像,从手机扫描,然后验证手机上的密码是正确。如果有人想加入,也许这可以作为图书馆进一步发展?代码可以在这里找到:

https://github.com/esp0/googleAuthNet

答案 2 :(得分:11)

要使用Google身份验证器添加Google双因素身份验证,您需要以下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Security.Cryptography;
using System.Text;
using System.Web.Profile;
using System.Web.Security;
using Google.Authenticator;

获取Google.Authenticator;点击这里https://www.nuget.org/packages/GoogleAuthenticator

现在设置Google身份验证。

TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
var setupInfo = tfa.GenerateSetupCode("Name of the app", "More info ABout the App", "SuperSecretKeyGoesHere", 300 , 300); //the width and height of the Qr Code in pixels

string qrCodeImageUrl = setupInfo.QrCodeSetupImageUrl; //  assigning the Qr code information + URL to string
string manualEntrySetupCode = setupInfo.ManualEntryKey; // show the Manual Entry Key for the users that don't have app or phone
Image1.ImageUrl = qrCodeImageUrl;// showing the qr code on the page "linking the string to image element"
Label1.Text = manualEntrySetupCode; // showing the manual Entry setup code for the users that can not use their phone

您可以将SuperSecretKeyGoesHere更改为您想要的任何值,但要确保它具有10个以上的字符,否则生成的手动输入键将不起作用。现在,您可以使用文本框和按钮单击

来检查用户输入

此位将查看用户条目并查看其是否正常

string user_enter=TextBox1.Text;
TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
bool isCorrectPIN = tfa.ValidateTwoFactorPIN("SuperSecretKeyGoesHere", user_enter);
if (isCorrectPIN == true)
{
Label2.Text = "i am cool";

}
else
{

Label2.Text = "i am Fool";
}

答案 3 :(得分:6)

这个问题要求提供一个教程,其他答案我觉得无法覆盖,

可以在以下网址找到:

http://www.codeproject.com/Articles/403355/Implementing-Two-Factor-Authentication-in-ASP-NET

该教程由Rick Bassham编写,涵盖以下信息:

“什么是双因素身份验证”“什么是Google身份验证器”“它是如何工作的”

然后解释了如何实现代码:

“基于计数器的一次性密码生成”“基于时间的一次性密码生成”

使用Visual Studio 2010提供完整的教程:

“我该如何使用”

答案 4 :(得分:2)

我没有找到教程,但似乎写一个端口不会那么难。它是一个Java应用程序,基于现有标准(HMAC SHA1)。

有关非GUI内容的详细信息,请参阅此页面:

请参阅这些页面以获取有关移植的信息以及现有(非官方)Silverlight端口:

答案 5 :(得分:1)

您可以运行此简单的控制台应用程序以了解如何验证一次性令牌代码。请注意,我们需要先从Nuget软件包安装库Otp.Net

static string secretKey = "JBSWY3DPEHPK3PXP"; //add this key to your Google Authenticator app  

private static void Main(string[] args)
{
        var bytes = Base32Encoding.ToBytes(secretKey);

        var totp = new Totp(bytes);

        while (true)
        {
            Console.Write("Enter your code from Google Authenticator app: ");
            string userCode = Console.ReadLine();

            //Generate one time token code
            string tokenInApp = totp.ComputeTotp();
            int remainingSeconds = totp.RemainingSeconds();

            if (userCode.Equals(tokenInApp)
                && remainingSeconds > 0)
            {
                Console.WriteLine("Success!");
            }
            else
            {
                Console.WriteLine("Failed. Try again!");
            }
        }
}