2011-06-21 14 views

Odpowiedz

9

Po kilku badaniach i testach stworzyłem własny "proof-of-concept", w jaki sposób można wygenerować obraz QR, zeskanować go z telefonu, a następnie sprawdzić, czy kod PIN na telefon jest poprawny. Może to może być dalej rozwijane jako biblioteka, jeśli ktoś chce się przyłączyć? Kod można znaleźć tutaj:

https://github.com/esp0/googleAuthNet

+0

To oni droga, dzięki! –

2

Nie znalazłem samouczka, ale wygląda na to, że napisanie portu nie byłoby takie trudne. Jest to aplikacja Java oparta na istniejących standardach (HMAC SHA1).

Zobacz aktualizacja informacji na temat wnętrzności non-GUI:

I zobaczyć tych stronach informacji o przenoszeniu, a istniejąca (nieoficjalny) Port Silverlight:

29

Podczas zabawy z aplikacji Google Authenticator, natknąłem się na ten temat, aw szczególności kodu wniesionego przez Espo. Osobiście nie byłem zadowolony z konwersji z Java na C#, więc pomyślałem, że udostępnię moją wersję. Oprócz mocno refaktoryzacji kodu:

  • Wprowadzony czek na ostrokońcej bajt zamawiania i konwertować do grubokońcej jak to konieczne.
  • Wprowadzony parametr dla klucza HMAC.

Aby uzyskać więcej informacji na temat formatu url rezerw, patrz również: https://github.com/google/google-authenticator/wiki/Key-Uri-Format

Zapraszam do korzystania, jeśli chcesz, a dzięki Espo do prac wstępnych.

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 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~"; 

      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 
} 
+0

Dziękujemy za ulepszenie mojego kodu. Nie było tak bardzo, jak zauważyłeś, ponieważ nie chciałem spędzać zbyt wiele czasu tylko na wykonanie POC. – Espo

+0

@ Michael Petito, czy masz to na GitHub? – devlord

+0

@lorddev, Nie, nie dodałem tego do GitHub. –

5

pytanie zadane za tutorial których inne odpowiedzi nie czuję okładka,

można znaleźć na stronie:

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

Tutorial został napisany przez Ricka Bassham i obejmuje informacje na temat:

"Co to jest uwierzytelnienie dwustopniowe" "Co to jest Google Authenticator" "Jak to działa"

Następnie wyjaśnia, jak zaimplementować kod:

"Counter Based One Time Generation hasło" "Time Based One Time Generation hasło"

i daje pełną samouczek przy użyciu programu Visual Studio 2010 na podstawie:

„Jak mogę umieścić go używać”

1

dodać dwa Factor Authentication Google za pomocą Google Authenticator potrzebne są następujące

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; 

Aby uzyskać Google.Authenticator; sprawdź tutaj https://www.nuget.org/packages/GoogleAuthenticator

teraz konfiguracja uwierzytelniania 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); 

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 

można zmienić SuperSecretKeyGoesHere na dowolną wartość, którą chcesz, ale upewnij się, że ma więcej niż 10 znaków w inny sposób ręcznego wprowadzania klucza, który jest generowany nie będzie działać. Teraz można sprawdzić dane wejściowe użytkownika z pola tekstowego i kliknij przycisk

ten bit będzie patrzeć na wejściu użytkownika i sprawdzić, czy jest 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"; 
}