我们经常会扫码绑定动态令牌,其实这些二维码的内容是otpauth url,其格式如下:
otpauth-uri = “otpauth://” uri-type “/” uri-label “?” uri-parameters
uri-type = “totp” / “hotp”
uri-label = *VCHAR 标签
uri-parameters = “secret=” *base-32 [ 必须包含sercret参数为编码为base32的字符串 ]
uri-opt-parameters = (“&algorithm=” 算法默认SHA1,可选SHA256、SHA512)
uri-opt-parameters =/ (“&digits=” 默认位数6)
uri-opt-parameters =/ (“&counter=” *DIGIT HOTP需要设置) / (“&period=” *DIGIT 默认30秒)
uri-opt-parameters =/ (“&issuer=” *VCHAR) / (“&” *VCHAR “=” *VCHAR) 颁发者
以下主要讨论的是TOTP,即基于时间的OTP
做一个算法为SM3的otpauth url很简单,设置下&algorithm=SM3即可,不过常用的这些扫码的app由于不支持SM3算法,所以不能正确显示动态密码。需要在OTP的服务器上支持生成SM3的密钥,支持SM3动态密码的生成,支持SM3动态口令的验证。需要在手机app端能扫码SM3的otpauth url,并能正确显示其动态密码。
国密SM3算法是公开的,可以自行开发实现。当然我们可以采用官方的轮子了,比如GmSSL-JS中的sm3.js就够用了,其实该文件还存在一些小问题,你用的时候自行修改即可。在最后一行要添加一句
module.exports = { sm3_kdf, sm3_hmac } 用来在其他js中调用
在手机端的话,除了sm3的函数需要调用,你其实还需要设计实现base32的解码或者引入一个轮子来解码。至于扫二维码识别,可以考虑app中调用系统相机的二维码识别或者微信小程序中直接调用微信扫码识别。前端就没有什么更多好说的了。
后端关于生成SM3密钥可以考虑随机生成一个长字符串比如长度为64?然后调用sm3_kdf这个密钥派生函数生成密钥,注意转换成base32码生成otpauth url的二维码,让用户手机扫码绑定。只是生成当前时间的动态密码还达不到OTP的功能要求,一般OTP还能允许预设定的前后几个时间窗口的密码验证通过,实现起来也不麻烦,不过可以直接利用其他现成的轮子改改,比如speakeasy的OTP,虽然默认不支持SM3,但是跟其他算法的主要区别就在摘要函数实现中,改改就可以支持SM3算法了。