go版本,google-authenticator动态口令算法,二次安全校验
登录安全二次校验,可以有效的提升账户安全等级,目前常用的方法:手机短信二次校验、动态口令
本文介绍google-authenticator动态口口令算法,以及加解密以及二维码生成
动态安全口令,秘钥一般通过二维码少吗形式自动获得
系统身份认证快速升级:
活体人脸实名认证H5版,让您的系统身份认证更人性化
一、生成和解密秘钥
随机生成字符串,加密转成base32,encoding.go
package mainimport ( "crypto/rand" "fmt" "log" "strconv" "strings")// Base32 字符表 33个字符var Base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="// SourceCreatSecret 生成秘钥:长度在16~128之间,从允许的base32字符中随机选择。func SourceCreatSecret(secretLen int) string { // 有效的秘密长度是80到640位:16*5~128*5,base32每个5位一个字节 if secretLen<16 || secretLen >128{ log.Fatalln("随机字符长度异常:16~128") } randomBytes := make([]byte, secretLen) _, err := rand.Read(randomBytes) if err != nil { log.Fatalln("随机字符异常:", err.Error()) } var secret string for _, i := range randomBytes { secret += string(Base32Chars[i&31]) } return secret}// SourceDecodeSecret 解码 Base32 编码的字符串func SourceDecodeSecret(secret string) string { padChar := Base32Chars[32:] paddingCharCount := strings.Count(secret, padChar) allowedValues := []int{6, 4, 3, 1, 0} if !InArray(paddingCharCount, allowedValues) { log.Fatalln("填充字符数量错误:", paddingCharCount) } // 校验秘钥带的填充字符数量,与实际应该填充的字符是否相同 for i := 0; i < 4; i++ { if paddingCharCount == allowedValues[i] { if secret[strings.Index(secret, padChar):] != strings.Repeat(padChar, allowedValues[i]) { log.Fatalln("填充字符错误:", secret[strings.Index(secret, padChar):]) } } } secret = strings.Replace(secret, "=", "", -1) // 每 8 个字符进行分组解码 var strs []byte for i := 0; i < len(secret); i += 8 { var x string // 查看当前字符是否是合法字符 if !strings.Contains(Base32Chars, string(secret[i])) { log.Fatalln("秘钥包含非法字符:", string(secret[i])) } // 将当前的8个字符转成二进制、拼接在一起,空值使用0 for j := 0; j < 8; j++ { // 将字符转成二进制 if len(secret) > (i + j) { x += fmt.Sprintf("%05b", strings.Index(Base32Chars, string(secret[i+j]))) } else { x += fmt.Sprintf("%05b", 0) } } l := len(x) for i := 0; i < l; i += 8 { end := i + 8 if end > l { end = l } } for i := 0; i < len(x)/8; i++ { it, _ := strconv.ParseInt(x[i*8:(i+1)*8], 2, 64) strs = append(strs, uint8(it)) } } return string(strs)}
2、生成动态口令和校验
动态口令生成和校验
package mainimport ( "crypto/hmac" "crypto/sha1" "encoding/binary" "fmt" "time")func InArray(check int, origin []int) bool { var exits bool for _, i := range origin { if i == check { exits = true break } } return exits}// getCode 根据解密的秘钥,获取动态口令码func GetCode(key string, timeSlice int) string { var code string time := make([]byte, 8) binary.BigEndian.PutUint64(time, uint64(timeSlice)) h := hmac.New(sha1.New, []byte(key)) h.Write(time) hm := h.Sum(nil) fmt.Println() // 计算偏移量 offset := hm[len(hm)-1] & 0x0F hashPart := hm[offset : offset+4] // 将哈希值解包为整数 value := binary.BigEndian.Uint32(hashPart) & 0x7FFFFFFF modulo := uint32(1000000) code = fmt.Sprintf("%06d", value%modulo) return code}// VerifyCode 动态码校验,其实就是生成当前时间戳,前后30秒的动态码,与输入的动态码进行对比;discrepancy:1:前后误差30秒;2:前后误差1分钟func VerifyCode(secret, code string, discrepancy, currentTimeSlice int) bool { if currentTimeSlice <= 0 { currentTimeSlice = int(time.Now().Unix() / 30) } if discrepancy < 1 { discrepancy = 1 } if len(code) != 6 { return false } for i := -discrepancy; i <= discrepancy; i++ { calculatedCode := GetCode(secret, currentTimeSlice+i) if calculatedCode == code { return true } } return false}// GetQrCode 生成二维码:secret(秘钥) ,accountName(用户名:必填), issuer(系统名称),appKey(聚合数据接口key)func GetQrCode(secret, accountName, issuer, appKey string) string { // otpauth://totp/Issuer:AccountName?secret=Base32Secret&issuer=IssuerName urlEncode := url.QueryEscape(fmt.Sprintf("otpauth://totp/%s?secret=%s", accountName, secret)) if issuer != "" { urlEncode += url.QueryEscape("&issuer=" + url.QueryEscape(issuer)) } return fmt.Sprintf("https://apis.juhe.cn/qrcode/api?key=%s&text=%s&w=200&h=200&el=m&type=2", appKey, urlEncode)}
三、使用
使用二维码生成,需要申请二维码生成
package mainimport ( "fmt" "math" "time")var ( secret string key string times int code string checked bool appKey string)
推荐本站淘宝优惠价购买喜欢的宝贝:
本文链接:https://zblog.hqyman.cn/post/11753.html 非本站原创文章欢迎转载,原创文章需保留本站地址!
打赏
微信支付宝扫一扫,打赏作者吧~


休息一下~~