Подтвердить что ты не робот

Есть ли учебное пособие по внедрению Google Authenticator в приложениях .NET?

Я ищу учебник по использованию Google Authenticator в .NET-приложениях. Существует ли это, и если да, где я могу его найти?

Я понимаю, что это можно использовать для добавления двухфакторной аутентификации в ваши собственные приложения.

4b9b3361

Ответ 1

После небольшого исследования и тестирования я создал свой собственный "доказательство концепции" о том, как вы можете генерировать QR-изображение, сканировать его со своего телефона, а затем проверить, что пин-код на телефоне верный. Может быть, это может быть развито дальше как библиотека, если кто-то хочет присоединиться? Код можно найти здесь:

https://github.com/esp0/googleAuthNet

Ответ 2

Играя с Google Authenticator, я столкнулся с этим вопросом и, в частности, с кодом, предоставленным Espo. Лично я не был удовлетворен переходом с Java на С# и поэтому решил поделиться своей версией. Помимо интенсивного рефакторинга кода:

  • Введена проверка порядка байтов с прямым порядком байтов и, при необходимости, преобразование в порядок с прямым порядком байтов.
  • Введен параметр для ключа HMAC.

Дополнительную информацию о формате URL-адреса предоставления см. Также по адресу: 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
}

Ответ 3

Чтобы добавить двухфакторную аутентификацию Google с помощью Google Authenticator, вам необходимо следующее

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";
}

Ответ 4

В вопросе задан вопрос о том, какие другие ответы я не чувствую,

можно найти по адресу:

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

Учебник был написан Риком Бассам и содержит информацию о:

"Что такое двухфакторная аутентификация" "Что такое Google Authenticator" "Как это работает"

Затем он объясняет, как реализовать код для:

"Генерация паролей с одноразовым генератором" "Генерация пароля, основанная на времени"

И дает полный учебник с использованием Visual Studio 2010 под:

"Как я могу использовать его"

Ответ 5

Я не нашел учебника, но, похоже, писать порт не так сложно. Это приложение Java и основано на существующих стандартах (HMAC SHA1).

См. эту страницу для получения подробной информации о не-GUI-кишках:

И посмотрите эти страницы для информации о портировании и существующий (неофициальный) порт Silverlight:

Ответ 6

Вы можете запустить это простое консольное приложение, чтобы понять, как проверить одноразовый код токена. Обратите внимание, что сначала нам нужно установить библиотеку Otp.Net из пакета Nuget.

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!");
            }
        }
}