1 using GoogleAuthorization;
2 using System;
3 using System.Security.Cryptography;
4 using System.Text;
5 namespace GoogleAuthenticator
6 {
7 public class GoogleAuthenticator
8 {
9 /// <summary>
10 /// 初始化验证码生成规则
11 /// </summary>
12 /// <param name="key">秘钥(手机使用Base32码)</param>
13 /// <param name="duration">验证码间隔多久刷新一次(默认30秒和google同步)</param>
14 public GoogleAuthenticator(long duration = 30, string key = "xeon997@foxmail.com")
15 {
16 this.SERECT_KEY = key;
17 this.SERECT_KEY_MOBILE = Base32.ToString(Encoding.UTF8.GetBytes(key));
18 this.DURATION_TIME = duration;
19 }
20
21 /// <summary>
22 /// 间隔时间
23 /// </summary>
24 private long DURATION_TIME { get; set; }
25
26 /// <summary>
27 /// 迭代次数
28 /// </summary>
29 private long COUNTER
30 {
31 get
32 {
33 return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds / DURATION_TIME;
34 }
35 }
36
37 /// <summary>
38 /// 秘钥
39 /// </summary>
40 private string SERECT_KEY { get; set; }
41
42 /// <summary>
43 /// 手机端输入的秘钥
44 /// </summary>
45 private string SERECT_KEY_MOBILE { get; set; }
46
47 /// <summary>
48 /// 到期秒数
49 /// </summary>
50 public long EXPIRE_SECONDS
51 {
52 get
53 {
54 return (DURATION_TIME - (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds % DURATION_TIME);
55 }
56 }
57
58 /// <summary>
59 /// 获取手机端秘钥
60 /// </summary>
61 /// <returns></returns>
62 public string GetMobilePhoneKey()
63 {
64 if (SERECT_KEY_MOBILE == null)
65 throw new ArgumentNullException("SERECT_KEY_MOBILE");
66 return SERECT_KEY_MOBILE;
67 }
68
69 /// <summary>
70 /// 生成认证码
71 /// </summary>
72 /// <returns>返回验证码</returns>
73 public string GenerateCode()
74 {
75 return GenerateHashedCode(SERECT_KEY, COUNTER);
76 }
77
78 /// <summary>
79 /// 按照次数生成哈希编码
80 /// </summary>
81 /// <param name="secret">秘钥</param>
82 /// <param name="iterationNumber">迭代次数</param>
83 /// <param name="digits">生成位数</param>
84 /// <returns>返回验证码</returns>
85 private string GenerateHashedCode(string secret, long iterationNumber, int digits = 6)
86 {
87 byte[] counter = BitConverter.GetBytes(iterationNumber);
88
89 if (BitConverter.IsLittleEndian)
90 Array.Reverse(counter);
91
92 byte[] key = Encoding.ASCII.GetBytes(secret);
93
94 HMACSHA1 hmac = new HMACSHA1(key, true);
95
96 byte[] hash = hmac.ComputeHash(counter);
97
98 int offset = hash[hash.Length - 1] & 0xf;
99
100 int binary =
101 ((hash[offset] & 0x7f) << 24)
102 | ((hash[offset + 1] & 0xff) << 16)
103 | ((hash[offset + 2] & 0xff) << 8)
104 | (hash[offset + 3] & 0xff);
105
106 int password = binary % (int)Math.Pow(10, digits); // 6 digits
107
108 return password.ToString(new string('0', digits));
109 }
110 }
111 }