Google Authenticator(谷歌身份验证器)C#版

mac2024-04-17  28

Google Authenticator(谷歌身份验证器)

什么是认证器?怎么对接?

Google Authenticator(谷歌身份验证器)是谷歌推出的一个动态密令工具,它有两种密令模式。分别是“TOTP 基于时间”、“HOTP 基于计数器”,通过手机上 简单的设置就可以设定自己独一的动态密令, 那么我们怎么将我们的程序和认证器进行对接呢?其实谷歌认证器并不是需要我们对接这个工具的API而是通过算法来决定,谷歌使用使用HMAC算法生成密令,通过基于次数或者基于时间两个模板进行计算,因此在程序中只需要使用相同的算法即可与之匹配。

TOTP 基于时间

HMAC算法使用固定为HmacSHA1更新时长固定为30秒APP端输入数据维度只有两个:账户名称(自己随意填写方便自己查看)和base32格式的key

HOTP 基于计数器

基于计数器模式是根据一个共享秘钥K和一个C计数器进行算法计算

认证器安装

手机需要安装认证器:

Android版:安卓版下载IOS版:苹果版下载

1、Base32.cs

using System; namespace GoogleAuthorization { public static class Base32 { public static byte[] ToBytes(string input) { if (string.IsNullOrEmpty(input)) { throw new ArgumentNullException("input"); } input = input.TrimEnd('='); int byteCount = input.Length * 5 / 8; byte[] 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 (arrayIndex != byteCount) { returnArray[arrayIndex] = curByte; } return returnArray; } 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 (arrayIndex != charCount) { returnArray[arrayIndex++] = ValueToChar(nextChar); while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; } return new string(returnArray); } private static int CharToValue(char c) { var value = (int)c; if (value < 91 && value > 64) { return value - 65; } if (value < 56 && value > 49) { return value - 24; } if (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"); } } }

2、GoogleAuthenticator.cs

using GoogleAuthorization; using System; using System.Security.Cryptography; using System.Text; namespace GoogleAuthenticator { public class GoogleAuthenticator { /// <summary> /// 初始化验证码生成规则 /// </summary> /// <param name="key">秘钥(手机使用Base32码)</param> /// <param name="duration">验证码间隔多久刷新一次(默认30秒和google同步)</param> public GoogleAuthenticator(string key) { this.SERECT_KEY = key; this.SERECT_KEY_MOBILE = Base32.ToString(Encoding.UTF8.GetBytes(key)); this.DURATION_TIME = 30; } /// <summary> /// 初始化验证码生成规则 /// </summary> /// <param name="key">秘钥(手机使用Base32码)</param> /// <param name="duration">验证码间隔多久刷新一次(默认30秒和google同步)</param> public GoogleAuthenticator(long duration = 30, string key = "test") { this.SERECT_KEY = key; this.SERECT_KEY_MOBILE = Base32.ToString(Encoding.UTF8.GetBytes(key)); this.DURATION_TIME = duration; } /// <summary> /// 间隔时间 /// </summary> private long DURATION_TIME { get; set; } /// <summary> /// 迭代次数 /// </summary> private long COUNTER { get { return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds / DURATION_TIME; } } /// <summary> /// 秘钥 /// </summary> private string SERECT_KEY { get; set; } /// <summary> /// 手机端输入的秘钥 /// </summary> private string SERECT_KEY_MOBILE { get; set; } /// <summary> /// 到期秒数 /// </summary> public long EXPIRE_SECONDS { get { return (DURATION_TIME - (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds % DURATION_TIME); } } /// <summary> /// 获取手机端秘钥 /// </summary> /// <returns></returns> public string GetMobilePhoneKey() { if (SERECT_KEY_MOBILE == null) throw new ArgumentNullException("SERECT_KEY_MOBILE"); return SERECT_KEY_MOBILE; } /// <summary> /// 生成认证码 /// </summary> /// <returns>返回验证码</returns> public string GenerateCode() { return GenerateHashedCode(SERECT_KEY, COUNTER); } /// <summary> /// 按照次数生成哈希编码 /// </summary> /// <param name="secret">秘钥</param> /// <param name="iterationNumber">迭代次数</param> /// <param name="digits">生成位数</param> /// <returns>返回验证码</returns> private string GenerateHashedCode(string secret, long iterationNumber, int digits = 6) { byte[] counter = BitConverter.GetBytes(iterationNumber); if (BitConverter.IsLittleEndian) Array.Reverse(counter); byte[] key = Encoding.ASCII.GetBytes(secret); HMACSHA1 hmac = new HMACSHA1(key, true); byte[] hash = hmac.ComputeHash(counter); int offset = hash[hash.Length - 1] & 0xf; int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); int password = binary % (int)Math.Pow(10, digits); // 6 digits return password.ToString(new string('0', digits)); } } }

3、使用方法

string user_Code = Request["Google_Code"].ToString(); string google_key = Common.PDDCommon.GetCode_GoogleAuthenticator(phone); if (string.IsNullOrEmpty(googleCode) || google_key.ToUpper() != googleCode.ToUpper()) { json = SerializerJson.SuccessFlagToJson(0, "谷歌验证码不正确!"); Response.Clear(); Response.Write(json); Response.End(); }

案例地址:

git:https://github.com/CN-Yi/GoogleAuthenticator

gitee:https://gitee.com/hsyi/GoogleAuthenticator

最新回复(0)