【SOLUTION】Java 生成 TOTP 验证码
OTP服务即:一次性密码服务,他有两种方式:
- HOTP:基于计数器的一次性密码服务,设备必须和服务端保同步计数器和约定共享密钥、算法,否则验证不通过
- TOTP:基于时间的一次性密码服务:设备和服务端约定时间的窗口期、共享密钥、算法;这种方法只需要确保客户端和服务端的时间是一致的就行(
推荐)
TOTP是现在使用最多的,因为不需要和服务器持续交互,只需要保持系统时间是一致的就行,所以本文主要讲解如何生成当前时间下的TOTP验证码:如下代码
@Override
public void createCode(TotpInfo info) {// 共享时间String secretKey = 'xxxxxxxxxx'; // 刷新间隔时间,单位:秒Integer interval =30;Date date = new Date();// 这里可以理解为和HOTP一样,计算当前可生成验证码的次数long time = date.getTime() / (interval * 1000);byte[] data = new byte[8];for (int i = 8; i-- > 0; time >>>= 8) {data[i] = (byte) time;}// HMACSHA1 算法,先通过Base32解码共享密钥HMac sha1 = SecureUtil.hmacSha1(Base32.decode(secretKey));// 获取时间摘要byte[] hash = sha1.digest(data);int offset = hash[hash.length - 1] & 0xF;long truncatedHash = 0;for (int i = 0; i < 4; ++i) {truncatedHash <<= 8;truncatedHash |= (hash[offset + i] & 0xFF);}truncatedHash &= 0x7FFFFFFF;truncatedHash %= 1000000;// 格式化codeString code=String.format("%06d", truncatedHash)}
注意:
- TOTP二维码内容格式如下:
- issuer: 颁发组织
- account:用户名称
- secretKey:共享密钥
# 不同的系统需要使用不同的issuer,防止account重复
otpauth://totp/{issuer}:{account}?secret={secretKey}&issuer={issuer}
- 共享密钥在每次重置TOTP绑定时,都会重新生成,防止之前的设备对重置后的TOTP依旧有效