此功能相当于给系统加了个令牌,只有输入对的一组数字才可以验证成功。类似于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");}public 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();}}

其中GenerateSetupCode 这个方法是用于把绑定的密钥直接转成二维码图片,然后再转成base64图片 输出再页面上,这样在APP上直接用扫一扫即可绑定。

二丶由于生成的密钥不可以直接使用,需要进行Base32进行编码。下面是Base32Encoding类

public class Base32Encoding{/// <summary>/// Base32 encoded string to byte[]/// </summary>/// <param name="input">Base32 encoded string</param>/// <returns>byte[]</returns>public static byte[] ToBytes(string input){if (string.IsNullOrEmpty(input)){throw new ArgumentNullException("input");}input = input.TrimEnd('='); //remove padding charactersint byteCount = input.Length * 5 / 8; //this must be TRUNCATEDbyte[] returnArray = new byte[byteCount];byte curByte = 0, bitsRemaining = 8;int mask = 0, arrayIndex = 0;foreach (char c in input){int cValue = CharToValue(c);if (bitsRemaining > 5){mask = cValue << (bitsRemaining - 5);curByte = (byte)(curByte | mask);bitsRemaining -= 5;}else{mask = cValue >> (5 - bitsRemaining);curByte = (byte)(curByte | mask);returnArray[arrayIndex++] = curByte;curByte = (byte)(cValue << (3 + bitsRemaining));bitsRemaining += 3;}}//if we didn't end with a full byteif (arrayIndex != byteCount){returnArray[arrayIndex] = curByte;}return returnArray;}/// <summary>/// byte[] to Base32 string, if starting from an ordinary string use Encoding.UTF8.GetBytes() to convert it to a byte[]/// </summary>/// <param name="input">byte[] of data to be Base32 encoded</param>/// <returns>Base32 String</returns>public static string ToString(byte[] input){if (input == null || input.Length == 0){throw new ArgumentNullException("input");}int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;char[] returnArray = new char[charCount];byte nextChar = 0, bitsRemaining = 5;int arrayIndex = 0;foreach (byte b in input){nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));returnArray[arrayIndex++] = ValueToChar(nextChar);if (bitsRemaining < 4){nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);returnArray[arrayIndex++] = ValueToChar(nextChar);bitsRemaining += 5;}bitsRemaining -= 3;nextChar = (byte)((b << bitsRemaining) & 31);}//if we didn't end with a full charif (arrayIndex != charCount){returnArray[arrayIndex++] = ValueToChar(nextChar);while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding}return new string(returnArray);}private static int CharToValue(char c){int value = (int)c;//65-90 == uppercase lettersif (value < 91 && value > 64){return value - 65;}//50-55 == numbers 2-7if (value < 56 && value > 49){return value - 24;}//97-122 == lowercase lettersif (value < 123 && value > 96){return value - 97;}throw new ArgumentException("Character is not a Base32 character.", "c");}private static char ValueToChar(byte b){if (b < 26){return (char)(b + 65);}if (b < 32){return (char)(b + 24);}throw new ArgumentException("Byte is not a value Base32 value.", "b");}}
三丶主程序里面直接调用方法
1
2
3
4
5
private SetupCode Google(string key, string Guids)
{GoogleAuthenticator gat = new GoogleAuthenticator();return gat.GenerateSetupCode("Supported Giving", key, Guids, 5);
}
//key系统的账号,Guid是进行加密的字符串,要求唯一,不然密钥会重复,所以这里使用Guid.   2为二维码的大小约120x120px。

SetupCode结果类为

public class SetupCode{public string Account { get; internal set; }public string AccountSecretKey { get; internal set; }public string ManualEntryKey { get; internal set; }/// <summary>/// Base64-encoded PNG image/// </summary>public string QrCodeSetupImageUrl { get; internal set; }
}

ManualEntryKey 是手机绑定的密钥。如果想手动输入密钥绑定就使用此字符串。
QrCodeSetupImageUrl 是将密钥转成的二维码图片

下载这个APP

进入APP后直接绑定,就会出现一下界面,即c#教程为绑定成功,然后我们就可以使用此令牌验证了。

验证方法

//Guids 之前生成密钥的字符,此时当做唯一键来查询,CheckCode为手机上动态的6位验证吗。校验成功会返回true

GoogleAuthenticator gat = new GoogleAuthenticator();
var result = gat.ValidateTwoFactorPIN(parameters["Guids"].ToString(), parameters["CheckCode"].ToString());
if (result)
{return "True";
}
else
{return "False";
}
GoogleAuthenticator gat = new GoogleAuthenticator();
var result = gat.ValidateTwoFactorPIN(parameters["Guids"].ToString(), parameters["CheckCode"].ToString());
if (result)
{return "True";
}
else
{return "False";
}

这样功能就完成了。

以上就是c# 使用谷歌身份验证GoogleAuthenticator的示例的详细内容

c# 使用谷歌身份验证GoogleAuthenticator的示例相关推荐

  1. 使用谷歌身份验证器增强SSH安全

    一般大家都是使用账号和密码远程SSH登录管理服务器.但SSH账号和密码很容易泄露,或者经常遭遇暴力破解.咨询过前同事赛赛,他们目前使用了谷歌身份验证器.查看了谷歌身份验证器的github和其它网上文档 ...

  2. 如何为SSH登录建立双因子验证机制(谷歌身份验证器)?

    前言 默认情况下,SSH已经在远程机器之间使用安全的数据通信;但是如果你想为自己的SSH连接添加另外某种安全层,可以添加谷歌身份验证器(Google Authenticator)双因子验证模块,该模块 ...

  3. 谷歌身份验证器代码实现

    手机下载谷歌身份验证器,无需联网也可以用. 工具类 public class GoogleAuthenticator{// 生成的key长度( Generate secret key length)p ...

  4. 谷歌身份验证器小白篇

    今天小白尝试了下谷歌身份验证器,记录下体会,谷歌身份验证器主要采用的是用户端跟后端独立生成验证码.验证码生成规则中有两个东西非常重要,一个是秘钥,一个时间,如果其中任意一个对不上,都会导致前后端验证码 ...

  5. 【SpringBoot】61、SpringBoot中使用谷歌身份验证器(Google Authenticator)实现二步身份验证

    Google 身份验证器 Google Authenticator 是谷歌推出的基于时间的一次性密码 (Time-based One-time Password,简称 TOTP),只需要在手机上安装该 ...

  6. Google authenticator 谷歌身份验证,实现动态口令

    Google authenticator 谷歌身份验证,实现动态口令 google authenticator php 服务端 使用PHP类 require_once '../PHPGangsta/G ...

  7. 使用谷歌身份验证器(Google Authenticator)保护你的后台

    为何要使用谷歌身份验证器 普通的网站只使用账号.密码.图形验证码进行后台登录.根据我(作为站长)多年的经验来看,这种方式安全性很低,尤其是使用 http 协议,明文的帐号和密码相当于在网络上裸奔.如果 ...

  8. 谷歌身份验证器 手表_6条使您的三星手表更加Google-y的提示

    谷歌身份验证器 手表 Samsung 三星 Samsung Galaxy watches are, arguably, the best smartwatches for Android phones ...

  9. 使用C++实现谷歌身份验证器(Google Authenticator)

    使用C++实现谷歌身份验证器(Google Authenticator) 本机环境: windows10 x64位运行环境 1.进入网站:http://slproweb.com/products/Wi ...

  10. 二次验证码小程序与谷歌身份验证器不同点是?

    名称1[二次验证码]小程序 名称2 谷歌身份验证器(Google Authenticator) 粗略对比两个产品异同 [二次验证码]小程序 搜索:微信搜索.微信目前65个小程序入口,倒是容易找到它 使 ...

最新文章

  1. ubuntu 安装GPU黑屏 修改GRUB_Ubuntu安装mysql后修改用户名和密码
  2. php语法大全下载,PHP下载文件
  3. IBM was调试端口开通演示,was服务重启、端口号查看方法
  4. try-catch语句讲解
  5. JS OOP -02 深入认识JS中的函数
  6. html5基础知识点常用标签
  7. 如何使用jquery的Highcharts
  8. 单片机外设基本概念_单片机基本概念
  9. 纽交所发言人就“GME事件”表态:采用先进技术监督市场
  10. java dom获取命名空间属性_Java DOM XML解析::获取元素属性值
  11. 【转】OUTLOOK签名档中加入写信日期
  12. php word 生成图片,使用PHPWord生成word文档
  13. 合并基因表达水平(merge gene expression levels, FPKM)
  14. DELMIA软件物流仿真:使用输送带输送物料的仿真操作方法
  15. Udacity-A/B TEST
  16. 用 GNS3 做CCNA网络实验(4)
  17. 解决高德地图api叠加Imagelayer播放雷达图闪烁
  18. 操作系统实验一:线程的创建与撤销
  19. 手机游戏毕业设计论文
  20. CodeForces - 768B Code For 1(找规律)

热门文章

  1. 520男生送什么礼物好、实用小礼物合集
  2. 兜兜转转还是回到程序员圈子
  3. 系统的x86与x64是什么意思以及他们的区别?
  4. 微软必应词典客户端的案例分析——个人Week3作业
  5. 如何建立地球上任何一个区域的地形3d模型,并添加卫星或地貌贴图
  6. AT32F435_437_USB_MSC_SDIO
  7. AdventureWorksCycle案例分析
  8. 大数据处理的四大步骤
  9. 第22章 常用存储器介绍
  10. 进程、线程和协程详解