1. Google验证器应用场景
(1)用户安装手机客户端,生成临时的身份验证码。
(2)用户将临时身份验证码提交到服务器验证身份。
(3)服务器确认用户身份,完成准入。
常见的使用场景:
- Google Apps验证
- 公司的软token登录服务器
应用:Authy
2. Google验证器实现原理
Google身份验证系统是通过基于时间的一次性密码算法实现的双因子(2FA)验证,即TOTP(Time-Based One-Time Password)算法。该算法由3部分组成:
- 一个共享密钥
- 一个基于当前时间的输入
- 一个签名函数
2.1 Shared Secret(共享密钥)
用户在手机端进行身份验证的时候,需要获取共享密钥。拿Authy举例,获取的方式包括识别程序扫描给定的二维码(QR)或者直接手动输入,密钥是32位加密的字符串。
(1)扫描QR用户,QR识别的URL链接为:
otpauth://totp/Google%3Ayourname@gmail.com?secret=xxxx&issuer=Google
(2)对于手动输入的用户,Google身份验证系统给出的共享密钥格式为:
xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
步骤如下:
- 用户开启双因子(2FA)验证后,服务器会生成一个密钥(secret)
- 服务器提示用户扫描二维码或者手动输入的方式,将密钥保存在用户的手机上。此时,用户和服务器现在都拥有同一把钥匙。
需要注意的是密钥必须跟手机绑定,一旦用户更换手机,需要重新生成全新的密钥
- 用户登录时,手机客户端Authy使用这个密钥和当前的Unix时间戳,生成一个hash value(h1),有效期默认为30s。用户在有效期内,将这个哈希值提交给服务器。
- 服务器也使用密钥和当前时间戳,生成一个hash value(h2),将h2和用户提交的h1进行比较,如果两者一致,就能够正常登陆,否则,拒绝登陆。
2.2 Input(Current Time)
简单来说,输入是基于用户手机产生的当前时间,一旦你获取到secret密钥,就不用再与服务器进行通信了。但是这里比较重要的是用户手机时间要准确,因为从算法原理来讲,身份验证服务器会基于同样的时间来重复进行用户手机的运算。进一步来说,服务器会计算当前时间前后几分钟内的令牌,跟用户提交的令牌比较。所以如果时间上相差太多,身份验证过程就会失败。
2.3 Signing Function(签名函数)
签名函数使用的是HMAC-SHA1。HMAC是基于哈希的消息验证码,能够用安全的单向哈希函数(SHA1)来生成签名。
验证算法的原理:只有共享密钥拥有者和服务器才能根据同样的输入(基于时间的)得到同样的输出签名。
hmac = SHA1(secret + SHA1(secret + input))
3. 算法
3.1 首先,使用base32的解码密钥
为了更方便用户输入,谷歌采用了空格和小写的方式表示密钥。但是base32不能有空格而且必须大写,伪代码如下:
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))
3.2 接下来,从当前时间获取输入,即Unix时间戳
input = CURRENT_UNIX_TIME()
一般验证码会有个失效时间,大概30s。这种设计主要是出于方便用户输入考虑,为了实现这种时效性,可以通过整除30的方式来实现。
input = CURRENT_UNIX_TIME() / 30
3.3 最后一步,签名函数(HMAC-SHA1)
伪代码如下:
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))
input = CURRENT_UNIX_TIME() / 30
hmac = SHA1(secret + SHA1(secret + input))
SHA1中的input转换为byte[8]的时候一定要是大端转换。
由于HMAC是个标准长度的SHA1数值,有20-byte,40 hex characters的长度,对于用户来说太长,所以google会根据规则截取6位数字。(也就是我们在Authy中看到的6个数字)
4. 最终的伪代码
original_secret
5. 代码实现及验证
5.1 举个栗子:Gitlab开启2FA验证 + Authy
(1)拥有一个Gitlab账号
在Account中,Register Two-Factor Authenticator
otpauth://totp/gitlab.com:410534805@qq.com?secret=4nygwpepxigvav6smazcz6574ghc5jvo&issuer=http://gitlab.com (二维码自动识别)
(2)手机端安装Authy App
用Authy扫描Gitlab中的二维码,Authy端就会根据当前时间戳生成一个6位数字的验证码,每隔30s变一次。
将验证码输入到(1)中的Pin code中,完成校验。
(3)退出登录,重新登录Gitlab,输入完用户+密码后,会提示进行2FA验证。
5.2 代码实现
代码详见:https://github.com/robbiev/two-factor-auth/blob/master/main.go
根据Gitlab生成的二维码,使用扫码工具识别出是以下URL
otpauth://totp/gitlab.com:410534805@qq.com?secret=4nygwpepxigvav6smazcz6574ghc5jvo&issuer=gitlab.com
我们知道secret=4nygwpepxigvav6smazcz6574ghc5jvo
接下来,我们运行程序
#会每30s变换一次Pin Code
此时,我们可以看到Authy生成的Pin Code,跟服务器生成的一致。