TOTP 验证码生成
按 RFC 6238 在本地算出两步验证的 6 位或 8 位动态口令,和 Google Authenticator、微软验证器显示的是同一串数字。
大小写、空格、连字符和结尾的 = 都会被忽略。密钥只在本地参与计算,不会上传。
Google Authenticator、微软验证器只认 SHA-1 和 6 位,用 SHA-256 或 8 位的场景(比如部分企业系统)需要和对方确认。
当前验证码
------
这个时间窗口还剩 15 秒,15 秒后自动换成新的
校验一个验证码
拿验证器 App 上显示的码填进来,比对一下两边的密钥是不是同一个。允许前后各一个窗口的时钟漂移。
它是怎么算出来的
TOTP 是「基于时间的一次性密码」。把当前 Unix 时间除以步长(默认 30 秒)取整,得到一条时间轴的编号,这个编号和密钥一起丢进 HMAC,对结果做动态截断,再取最后 6 位或 8 位数字。所以只要两边的密钥、算法、步长和时钟一致,算出的码就一定相同,不需要联网。
计算用的是浏览器自带的 Web Crypto(crypto.subtle),整站的密钥和验证码都不会离开这台设备。也正因为它只依赖本地时钟,设备时间偏差超过一个窗口(默认 30 秒)就会一直提示验证码错误,遇到这种情况先看一眼手机和电脑的时间同步。
密钥是 Base32 编码的(RFC 4648),字母表是 A–Z 和 2–7。验证器 App 扫二维码时拿到的 otpauth:// 链接里,secret= 后面就是这串东西。本页生成的随机密钥是 20 字节,折算成 160 位,和各家验证器的默认长度一致,可以直接填进 App 或写进代码。
RFC 6238 附录 B 给了标准测试向量:密钥取 ASCII 的 12345678901234567890,在 T=59 秒时 SHA-1 的 8 位结果应该是 94287082。本站的实现用这些向量做过自测,你也可以把上面的密钥换成 GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ 验证一下。
最后提醒一句:把真实密钥粘到任何网页里都有风险,即使是纯前端页面,也可能被别人看到屏幕或被浏览器插件读取。常用的服务还是老老实实用验证器 App。