From c697de539f6d2b2a767a2a74fe41f26499b47f11 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 30 Apr 2020 12:01:35 +0800 Subject: [PATCH] add TOPT --- .../cn/hutool/crypto/digest/opt/HOTP.java | 6 +- .../cn/hutool/crypto/digest/opt/TOPT.java | 86 +++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/TOPT.java diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java index fe69e2006..4060d0727 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/HOTP.java @@ -34,14 +34,14 @@ public class HOTP { private final byte[] buffer; /** - * 构造,使用默认密码长度和默认HMAC算法 + * 构造,使用默认密码长度和默认HMAC算法(HmacSHA1) */ public HOTP(byte[] key) { this(DEFAULT_PASSWORD_LENGTH, key); } /** - * 构造,使用默认HMAC算法 + * 构造,使用默认HMAC算法(HmacSHA1) * * @param passwordLength 密码长度,可以是6,7,8 * @param key 共享密码,RFC 4226要求最少128位 @@ -72,7 +72,7 @@ public class HOTP { * 可以是基于计次的动移动因子,也可以是计时移动因子 * @return 一次性密码的int值 */ - public synchronized int generateOneTimePassword(final long counter) { + public synchronized int generate(final long counter) { // C 的整数值需要用二进制的字符串表达,比如某个事件计数为 3, // 则C是 "11"(此处省略了前面的二进制的数字0) this.buffer[0] = (byte) ((counter & 0xff00000000000000L) >>> 56); diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/TOPT.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/opt/TOPT.java new file mode 100644 index 000000000..84ec7b5f5 --- /dev/null +++ b/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; + +/** + *

time-based one-time passwords (TOTP) 一次性密码生成器, + * 规范见:RFC 6238.

+ * + *

参考:https://github.com/jchambers/java-otp

+ * + * @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; + } +}