• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

c# 使用谷歌身份验证GoogleAuthenticator的示例

c# 搞代码 4年前 (2022-01-09) 45次浏览 已收录 0个评论
文章目录[隐藏]

此功能相当于给系统加了个令牌,只有输入对的一组数字才可以验证成功。类似于QQ令牌一样。

一丶创建最核心的一个类GoogleAuthenticator

此类包含了生成密钥,验证,将绑定密钥转为二维码。

public class GoogleAuthenticator
  {
    private readonly static DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private TimeSpan DefaultClockDriftTolerance { get; set; }

    public GoogleAuthenticator()
    {
      DefaultClockDriftTolerance = TimeSpan.FromMinutes(5);
    }

    /// <summary>
    /// Generate a setup code for a Google Authenticator user to scan
    /// </summary>
    /// <param name="issuer">Issuer ID (the name of the system, i.e. 'MyApp'), can be omitted but not recommended https://github.com/google/google-authenticator/wiki/Key-Uri-Format </param>
    /// <param name="accountTitleNoSpaces">Account Title (no spaces)</param>
    /// <param name="accountSecretKey">Account Secret Key</param>
    /// <param name="QRPixelsPerModule">Number of pixels per QR Module (2 pixels give ~ 100x100px QRCode)</param>
    /// <returns>SetupCode object</returns>
    public SetupCode GenerateSetupCode(string issuer, string accountTitleNoSpaces, string accountSecretKey, int QRPixelsPerModule)
    {
      byte[] key = Encoding.UTF8.GetBytes(accountSecretKey);
      return GenerateSetupCode(issuer, accountTitleNoSpaces, key, QRPixelsPerModule);
    }

    /// <summary>
    /// Generate a setup code for a Google Authenticator user to scan
    /// </summary>
    /// <param name="issuer">Issuer ID (the name of the system, i.e. 'MyApp'), can be omitted but not recommended https://github.com/google/google-authenticator/wiki/Key-Uri-Format </param>
    /// <param name="accountTitleNoSpaces">Account Title (no spaces)</param>
    /// <param name="accountSecretKey">Account Secret Key as byte[]</param>
    /// <param name="QRPixelsPerModule">Number of pixels per QR Module (2 = ~120x120px QRCode)</param>
    /// <returns>SetupCode object</returns>
    public SetupCode GenerateSetupCode(string issuer, string accountTitleNoSpaces, byte[] accountSecretKey, int QRPixelsPerModule)
    {
      if (accountTitleNoSpaces == null) { throw new NullReferenceException("Account Title is null"); }
      accountTitleNoSpaces = RemoveWhitespace(accountTitleNoSpaces);
      string encodedSecretKey = Base32Encoding.ToString(accountSecretKey);
      string provisionUrl = null;
      provisionUrl = String.Format("otpauth://totp/{2}:{0}?secret={1}&issuer={2}", accountTitleNoSpaces, encodedSecretKey.Replace("=",""), UrlEncode(issuer));



      using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
      using (QRCodeData qrCodeData = qrGenerator.CreateQrCode(provisionUrl, QRCodeGenerator.ECCLevel.M))
      using (QRCode qrCode = new QRCode(qrCodeData))
      using (Bitmap qrCodeImage = qrCode.GetGraphic(QRPixelsPerModule))
      using (MemoryStream ms = new MemoryStream())
      {
        qrCodeImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);

        return new SetupCode(accountTitleNoSpaces, encodedSecretKey, String.Format("data:image/png;base64,{0}", Convert.ToBase64String(ms.ToArray())));
      }

    }

    private static string RemoveWhitespace(string str)
    {
      return new string(str.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }

    private string UrlEncode(string value)
    {
      StringBuilder result = new StringBuilder();
      string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

      foreach (char symbol in value)
      {
        if (validChars.IndexOf(symbol) != -1)
        {
          result.Append(symbol);
        }
        else
        {
          result.Append('%' + String.Format("{0:X2}", (int)symbol));
        }
      }

      return result.ToString().Replace(" ", "%20");
    }

    pu<strong>本文来源gao@daima#com搞(%代@#码@网2</strong>blic string GeneratePINAtInterval(string accountSecretKey, long counter, int digits = 6)
    {
      return GenerateHashedCode(accountSecretKey, counter, digits);
    }

    internal string GenerateHashedCode(string secret, long iterationNumber, int digits = 6)
    {
      byte[] key = Encoding.UTF8.GetBytes(secret);
      return GenerateHashedCode(key, iterationNumber, digits);
    }

    internal string GenerateHashedCode(byte[] key, long iterationNumber, int digits = 6)
    {
      byte[] counter = BitConverter.GetBytes(iterationNumber);

      if (BitConverter.IsLittleEndian)
      {
        Array.Reverse(counter);
      }

      HMACSHA1 hmac = new HMACSHA1(key);

      byte[] hash = hmac.ComputeHash(counter);

      int offset = hash[hash.Length - 1] & 0xf;

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

      int password = binary % (int)Math.Pow(10, digits);
      return password.ToString(new string('0', digits));
    }

    private long GetCurrentCounter()
    {
      return GetCurrentCounter(DateTime.UtcNow, _epoch, 30);
    }

    private long GetCurrentCounter(DateTime now, DateTime epoch, int timeStep)
    {
      return (long)(now - epoch).TotalSeconds / timeStep;
    }

    public bool ValidateTwoFactorPIN(string accountSecretKey, string twoFactorCodeFromClient)
    {
      return ValidateTwoFactorPIN(accountSecretKey, twoFactorCodeFromClient, DefaultClockDriftTolerance);
    }

    public bool ValidateTwoFactorPIN(string accountSecretKey, string twoFactorCodeFromClient, TimeSpan timeTolerance)
    {
      var codes = GetCurrentPINs(accountSecretKey, timeTolerance);
      return codes.Any(c => c == twoFactorCodeFromClient);
    }

    public string[] GetCurrentPINs(string accountSecretKey, TimeSpan timeTolerance)
    {
      List<string> codes = new List<string>();
      long iterationCounter = GetCurrentCounter();
      int iterationOffset = 0;

      if (timeTolerance.TotalSeconds > 30)
      {
        iterationOffset = Convert.ToInt32(timeTolerance.TotalSeconds / 30.00);
      }

      long iterationStart = iterationCounter - iterationOffset;
      long iterationEnd = iterationCounter + iterationOffset;

      for (long counter = iterationStart; counter <= iterationEnd; counter++)
      {
        codes.Add(GeneratePINAtInterval(accountSecretKey, counter));
      }

      return codes.ToArray();
    }
  }

搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:c# 使用谷歌身份验证GoogleAuthenticator的示例
喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址