Google Authenticator 根据秘钥(不是种子)生成码

2 篇文章 0 订阅
2 篇文章 0 订阅

忘了在哪里看到的了,如果作者看到请谅解,
自用好长一段时间了,因为做爬虫用到,想起来了就分享出来 

public class GoogleAuthenticator
    {
        /// <summary>
        /// 初始化验证码生成规则
        /// </summary>
        /// <param name="key">秘钥(手机使用Base32码),不可以为空</param>
        /// <param name="duration">验证码间隔多久刷新一次(默认30秒和google同步)</param>
        public GoogleAuthenticator(long duration = 30, string key = "SGOUNKTWDYUJ5RUX")
        {
            this.SERECT_KEY = key.ToLower();
            // this.SERECT_KEY_MOBILE = Base32Encoding.ToString(Encoding.UTF8.GetBytes(key));

            this.SERECT_KEY_MOBILE = 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);
        }

        public string GetMobleCode()
        {
            return GenerateMobleHashedCode(SERECT_KEY_MOBILE);
        }

        /// <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));
        }

        private string GenerateMobleHashedCode(string secret, int digits = 6)
        {
            byte[] counter = BitConverter.GetBytes(COUNTER);

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

            byte[] key = Base32Encoding.ToBytes(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));
        }

    }

    public static class Base32Encoding
    {
        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");
        }
    }

用法:

GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(30, "xxxxxxx");
var mobileKey = googleAuthenticator.GetMobilePhoneKey();
string gcode = googleAuthenticator.GetMobleCode();

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值