I'm looking for a tutorial on how to use Google Authenticator in .NET-apps. Does this exist, and if so, where can I find it?
I understand that this can be used to add two-factor-authentication to your own apps.
Sign into your Google Account and navigate to Security tab. In the security page, click on the option “2-steps verification”, that will take you to the page where you can set up your preferable 2FA options. Find the Authenticator app option and click on set up. You will get a popup to choose your device type.
Simply rebooting your mobile device may resolve the problem, or disabling Google Authenticator for your account and then re-enabling it may also help.
While playing around with Google Authenticator, I came across this question and in particular the code contributed by Espo. I personally wasn't satisfied with the conversion from Java to C# and so I thought I would share my version. Aside from heavily refactoring the code:
For more information on the provisioning url format, see also: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
Feel free to use if you like, and thanks to Espo for the initial work.
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
}
To Add Google Two Factor Authentication using Google Authenticator you need the following
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;
To get the Google.Authenticator; check here https://www.nuget.org/packages/GoogleAuthenticator
now setting up the Google authentication.
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
you can change the SuperSecretKeyGoesHere to any value that you want, but make sure it has more than 10 character otherwise the manual entry key that is generated will not work. Now you can check the user input with text box and button click
this bit will look at the user entry and see if its ok
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";
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With