Looly 5 years ago
parent
commit
c697de539f

+ 3 - 3
hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java

@@ -34,14 +34,14 @@ public class HOTP {
 	private final byte[] buffer;
 	private final byte[] buffer;
 
 
 	/**
 	/**
-	 * 构造,使用默认密码长度和默认HMAC算法
+	 * 构造,使用默认密码长度和默认HMAC算法(HmacSHA1)
 	 */
 	 */
 	public HOTP(byte[] key) {
 	public HOTP(byte[] key) {
 		this(DEFAULT_PASSWORD_LENGTH, key);
 		this(DEFAULT_PASSWORD_LENGTH, key);
 	}
 	}
 
 
 	/**
 	/**
-	 * 构造,使用默认HMAC算法
+	 * 构造,使用默认HMAC算法(HmacSHA1)
 	 *
 	 *
 	 * @param passwordLength 密码长度,可以是6,7,8
 	 * @param passwordLength 密码长度,可以是6,7,8
 	 * @param key            共享密码,RFC 4226要求最少128位
 	 * @param key            共享密码,RFC 4226要求最少128位
@@ -72,7 +72,7 @@ public class HOTP {
 	 *                可以是基于计次的动移动因子,也可以是计时移动因子
 	 *                可以是基于计次的动移动因子,也可以是计时移动因子
 	 * @return 一次性密码的int值
 	 * @return 一次性密码的int值
 	 */
 	 */
-	public synchronized int generateOneTimePassword(final long counter) {
+	public synchronized int generate(final long counter) {
 		// C 的整数值需要用二进制的字符串表达,比如某个事件计数为 3,
 		// C 的整数值需要用二进制的字符串表达,比如某个事件计数为 3,
 		// 则C是 "11"(此处省略了前面的二进制的数字0)
 		// 则C是 "11"(此处省略了前面的二进制的数字0)
 		this.buffer[0] = (byte) ((counter & 0xff00000000000000L) >>> 56);
 		this.buffer[0] = (byte) ((counter & 0xff00000000000000L) >>> 56);

+ 86 - 0
hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/TOPT.java

@@ -0,0 +1,86 @@
+package cn.hutool.crypto.digest.opt;
+
+import cn.hutool.crypto.digest.HmacAlgorithm;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * <p>time-based one-time passwords (TOTP) 一次性密码生成器,
+ * 规范见:<a href="https://tools.ietf.org/html/rfc6238">RFC&nbsp;6238</a>.</p>
+ *
+ * <p>参考:https://github.com/jchambers/java-otp</p>
+ *
+ * @author Looly
+ */
+public class TOPT extends HOTP {
+
+	/**
+	 * 默认步进 (30秒).
+	 */
+	public static final Duration DEFAULT_TIME_STEP = Duration.ofSeconds(30);
+
+	private final Duration timeStep;
+
+	/**
+	 * 构造,使用默认HMAC算法(HmacSHA1)
+	 *
+	 * @param key 共享密码,RFC 4226要求最少128位
+	 */
+	public TOPT(byte[] key) {
+		this(DEFAULT_TIME_STEP, key);
+	}
+
+	/**
+	 * 构造,使用默认HMAC算法(HmacSHA1)
+	 *
+	 * @param timeStep 日期步进,用于生成移动因子(moving factor)
+	 * @param key      共享密码,RFC 4226要求最少128位
+	 */
+	public TOPT(Duration timeStep, byte[] key) {
+		this(timeStep, DEFAULT_PASSWORD_LENGTH, key);
+	}
+
+	/**
+	 * 构造,使用默认HMAC算法(HmacSHA1)
+	 *
+	 * @param timeStep       日期步进,用于生成移动因子(moving factor)
+	 * @param passwordLength 密码长度,可以是6,7,8
+	 * @param key            共享密码,RFC 4226要求最少128位
+	 */
+	public TOPT(Duration timeStep, int passwordLength, byte[] key) {
+		this(timeStep, passwordLength, HOTP_HMAC_ALGORITHM, key);
+	}
+
+	/**
+	 * 构造
+	 *
+	 * @param timeStep       日期步进,用于生成移动因子(moving factor)
+	 * @param passwordLength 密码长度,可以是6,7,8
+	 * @param algorithm      HMAC算法枚举
+	 * @param key            共享密码,RFC 4226要求最少128位
+	 */
+	public TOPT(Duration timeStep, int passwordLength, HmacAlgorithm algorithm, byte[] key) {
+		super(passwordLength, algorithm, key);
+		this.timeStep = timeStep;
+	}
+
+	/**
+	 * 使用给定的时间戳生成一次性密码.
+	 *
+	 * @param timestamp 用于生成密码的时间戳
+	 * @return 一次性密码的int形式
+	 */
+	public int generate(Instant timestamp) {
+		return this.generate(timestamp.toEpochMilli() / this.timeStep.toMillis());
+	}
+
+	/**
+	 * 获取步进
+	 *
+	 * @return 步进
+	 */
+	public Duration getTimeStep() {
+		return this.timeStep;
+	}
+}