org.nervousync.utils.OTPUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of utils-jdk11 Show documentation
Show all versions of utils-jdk11 Show documentation
Java utility collections, development by Nervousync Studio (NSYC)
/*
* Licensed to the Nervousync Studio (NSYC) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nervousync.utils;
import org.nervousync.commons.Globals;
import org.nervousync.exceptions.utils.DataInvalidException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* OTP(One-time Password Algorithm) Utilities
*
* Current utilities implements features:
* Calculate OTP fixed time value
* Generate random key
* Generate TOTP/HOTP Code
* Verify TOTP/HOTP Code
*
* 一次性密码算法工具集
*
* 此工具集实现以下功能:
* 计算一次性密码算法的修正时间值
* 生成随机密钥
* 生成基于HMAC算法加密的一次性密码/基于时间戳算法的一次性密码值
* 验证基于HMAC算法加密的一次性密码/基于时间戳算法的一次性密码值
*
*
* @author Steven Wee [email protected]
* @version $Revision: 1.1.2 $ $Date: Jun 04, 2019 10:47:28 $
*/
public final class OTPUtils {
/**
* Logger instance
* 日志实例
*/
private static final LoggerUtils.Logger LOGGER = LoggerUtils.getLogger(OTPUtils.class);
// Unit: Second
/**
* Time Step, Unit: Second
* 时间步长,单位:秒
*/
private static final int DEFAULT_SYNC_COUNT = 30;
// Default 3, Maximum 17 (From Google Docs)
/**
* Default window size
* 默认的最多可偏移时间
*/
private static final int DEFAULT_WINDOW_SIZE = 3;
/**
* Default secret size
* 默认的密钥长度
*/
private static final int DEFAULT_SECRET_SIZE = 10;
/**
* Default secret seed character
* 默认的密钥种子字符
*/
private static final String DEFAULT_SECRET_SEED = "TmVydm91c3luY0RlZmF1bHRTZWNyZXRTZWVk";
/**
* Default random algorithm
* 默认的随机数算法
*/
private static final String DEFAULT_RANDOM_ALGORITHM = "SHA1PRNG";
/**
* Private constructor for OTPUtils
* 一次性密码算法工具集的私有构造方法
*/
private OTPUtils() {
}
/**
* Calculate fixed time
* 计算修正时间
*
* @param secret Secret key string
* 随机密钥字符串
* @param authCode Client generated authenticate code
* 客户端生成的随机验证码
*
* @return Calculated fixed time
* 计算出的修正时间
*/
public static long calculateFixedTime(final String secret, final int authCode) {
return calculateFixedTime(CalcType.HmacSHA1, secret, authCode, Globals.DEFAULT_VALUE_INT);
}
/**
* Calculate fixed time
* 计算修正时间
*
* @param secret Secret key string
* 随机密钥字符串
* @param authCode Client generated authenticate code
* 客户端生成的随机验证码
* @param syncCount Time Step, Unit: Second
* 时间步长,单位:秒
*
* @return Calculated fixed time
* 计算出的修正时间
*/
public static long calculateFixedTime(final String secret, final int authCode, final int syncCount) {
return calculateFixedTime(CalcType.HmacSHA1, secret, authCode, syncCount);
}
/**
* Calculate fixed time
* 计算修正时间
*
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param authCode Client generated authenticate code
* 客户端生成的随机验证码
*
* @return Calculated fixed time
* 计算出的修正时间
*/
public static long calculateFixedTime(final CalcType calcType, final String secret, final int authCode) {
return calculateFixedTime(calcType, secret, authCode, Globals.DEFAULT_VALUE_INT);
}
/**
* Calculate fixed time
* 计算修正时间
*
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param authCode Client generated authenticate code
* 客户端生成的随机验证码
* @param syncCount Time Step, Unit: Second
* 时间步长,单位:秒
*
* @return Calculated fixed time
* 计算出的修正时间
*/
public static long calculateFixedTime(final CalcType calcType, final String secret,
final int authCode, final int syncCount) {
for (int i = -12 ; i <= 12 ; i++) {
long fixedTime = i * 60 * 60 * 1000L;
if (validateTOTPCode(authCode, calcType, secret, fixedTime, syncCount, Globals.INITIALIZE_INT_VALUE)) {
return fixedTime;
}
}
return Globals.DEFAULT_VALUE_INT;
}
/**
* Generate TOTP(Time-based One-time Password) code
* 生成基于时间的一次性密码
*
* @param secret Secret key string
* 随机密钥字符串
*
* @return Generated code
* 生成的一次性密码
*/
public static String generateTOTPCode(final String secret) {
return generateTOTPCode(CalcType.HmacSHA1, secret, Globals.INITIALIZE_INT_VALUE, Globals.DEFAULT_VALUE_INT);
}
/**
* Generate TOTP(Time-based One-time Password) code
* 生成基于时间的一次性密码
*
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
*
* @return Generated code
* 生成的一次性密码
*/
public static String generateTOTPCode(final String secret, final long fixedTime) {
return generateTOTPCode(CalcType.HmacSHA1, secret, fixedTime, Globals.DEFAULT_VALUE_INT);
}
/**
* Generate TOTP(Time-based One-time Password) code
* 生成基于时间的一次性密码
*
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
* @param syncCount Time Step, Unit: Second
* 时间步长,单位:秒
*
* @return Generated code
* 生成的一次性密码
*/
public static String generateTOTPCode(final String secret, final long fixedTime, final int syncCount) {
return generateTOTPCode(CalcType.HmacSHA1, secret, fixedTime, syncCount);
}
/**
* Generate TOTP(Time-based One-time Password) code
* 生成基于时间的一次性密码
*
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
*
* @return Generated code
* 生成的一次性密码
*/
public static String generateTOTPCode(final CalcType calcType, final String secret, final long fixedTime) {
return generateTOTPCode(calcType, secret, fixedTime, Globals.DEFAULT_VALUE_INT);
}
/**
* Generate TOTP(Time-based One-time Password) code
* 生成基于时间的一次性密码
*
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
* @param syncCount Time Step, Unit: Second
* 时间步长,单位:秒
*
* @return Generated code
* 生成的一次性密码
*/
public static String generateTOTPCode(final CalcType calcType, final String secret,
final long fixedTime, final int syncCount) {
int authCode = OTPUtils.generateTOTPCode(calcType, secret,
fixedTime, syncCount, Globals.INITIALIZE_INT_VALUE);
if (authCode == Globals.DEFAULT_VALUE_INT) {
return Globals.DEFAULT_VALUE_STRING;
}
StringBuilder returnCode = new StringBuilder(Integer.toString(authCode));
while (returnCode.length() < 6) {
returnCode.insert(0, "0");
}
return returnCode.toString();
}
/**
* Validate TOTP(Time-based One-time Password) code
* 验证基于时间的一次性密码
*
* @param authCode Authenticate code
* 需要验证的一次性密码
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
*
* @return Validate result
* 验证结果
*/
public static boolean validateTOTPCode(final int authCode, final String secret, final long fixedTime) {
return validateTOTPCode(authCode, CalcType.HmacSHA1, secret, fixedTime,
Globals.DEFAULT_VALUE_INT, Globals.DEFAULT_VALUE_INT);
}
/**
* Validate TOTP(Time-based One-time Password) code
* 验证基于时间的一次性密码
*
* @param authCode Authenticate code
* 需要验证的一次性密码
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
* @param fixWindow Fix window size
* 最多可偏移时间
*
* @return Validate result
* 验证结果
*/
public static boolean validateTOTPCode(final int authCode, final String secret,
final long fixedTime, final int fixWindow) {
return validateTOTPCode(authCode, CalcType.HmacSHA1, secret, fixedTime,
Globals.DEFAULT_VALUE_INT, fixWindow);
}
/**
* Validate TOTP(Time-based One-time Password) code
* 验证基于时间的一次性密码
*
* @param calcType Calculate type
* 密码算法类型
* @param authCode Authenticate code
* 需要验证的一次性密码
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
* @param fixWindow Fix window size
* 最多可偏移时间
*
* @return Validate result
* 验证结果
*/
public static boolean validateTOTPCode(final int authCode, final CalcType calcType, final String secret,
final long fixedTime, final int syncCount, final int fixWindow) {
if (authCode > Globals.INITIALIZE_INT_VALUE) {
int minWindow = fixWindow < 0 ? (-1 * DEFAULT_WINDOW_SIZE) : (-1 * fixWindow);
int maxWindow = fixWindow < 0 ? DEFAULT_WINDOW_SIZE : fixWindow;
for (int i = minWindow ; i <= maxWindow ; i++) {
int generateCode = generateTOTPCode(calcType, secret, fixedTime, syncCount, i);
if (generateCode == authCode) {
return true;
}
}
}
return Boolean.FALSE;
}
/**
* Generate HOTP(HMAC-based One-time Password) code
* 生成基于HMAC算法加密的一次性密码
*
* @param secret Secret key string
* 随机密钥字符串
* @param randomCode Random number
* 随机数
*
* @return Generated code
* 生成的一次性密码
*/
public static int generateHOTPCode(final String secret, final long randomCode) {
return generateCode(CalcType.HmacSHA1, secret, randomCode);
}
/**
* Generate HOTP(HMAC-based One-time Password) code
* 生成基于HMAC算法加密的一次性密码
*
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param randomCode Random number
* 随机数
*
* @return Generated code
* 生成的一次性密码
*/
public static int generateHOTPCode(final CalcType calcType, final String secret, final long randomCode) {
return generateCode(calcType, secret, randomCode);
}
/**
* Validate HOTP(HMAC-based One-time Password) code
* 验证基于HMAC算法加密的一次性密码
*
* @param authCode Authenticate code
* 需要验证的一次性密码
* @param secret Secret key string
* 随机密钥字符串
* @param randomCode Random number
* 随机数
*
* @return Validate result
* 验证结果
*/
public static boolean validateHOTPCode(final int authCode, final String secret, final long randomCode) {
return authCode > Globals.INITIALIZE_INT_VALUE
? authCode == generateHOTPCode(CalcType.HmacSHA1, secret, randomCode)
: Boolean.FALSE;
}
/**
* Validate HOTP(HMAC-based One-time Password) code
* 验证基于HMAC算法加密的一次性密码
*
* @param authCode Authenticate code
* 需要验证的一次性密码
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param randomCode Random number
* 随机数
*
* @return Validate result
* 验证结果
*/
public static boolean validateHOTPCode(final int authCode, final CalcType calcType, final String secret,
final long randomCode) {
return authCode > Globals.INITIALIZE_INT_VALUE
? authCode == generateHOTPCode(calcType, secret, randomCode)
: Boolean.FALSE;
}
/**
* Generate random secret key string
* 生成随机的密码字符串
*
* @return Generated secret key string
* 生成的密码字符串
*/
public static String generateRandomKey() {
return generateRandomKey(DEFAULT_RANDOM_ALGORITHM, DEFAULT_SECRET_SEED, Globals.DEFAULT_VALUE_INT);
}
/**
* Generate random secret key string
* 生成随机的密码字符串
*
* @param size Secret size
* 密钥长度
*
* @return Generated secret key string
* 生成的密码字符串
*/
public static String generateRandomKey(final int size) {
return generateRandomKey(DEFAULT_RANDOM_ALGORITHM, DEFAULT_SECRET_SEED, size);
}
/**
* Generate random secret key string
* 生成随机的密码字符串
*
* @param algorithm Secure random algorithm
* 安全随机数算法
* @param seed Secret seed character
* 密钥种子字符
* @param size Secret size
* 密钥长度
*
* @return Generated secret key string
* 生成的密码字符串
*/
public static String generateRandomKey(final String algorithm, final String seed, final int size) {
String randomKey = null;
try {
SecureRandom secureRandom = StringUtils.notBlank(algorithm)
? SecureRandom.getInstance(algorithm)
: SecureRandom.getInstance(DEFAULT_RANDOM_ALGORITHM);
if (StringUtils.notBlank(seed)) {
secureRandom.setSeed(StringUtils.base64Decode(seed));
}
byte[] randomKeyBytes =
secureRandom.generateSeed(size == Globals.DEFAULT_VALUE_INT ? DEFAULT_SECRET_SIZE : size);
randomKey = StringUtils.base32Encode(randomKeyBytes, Boolean.FALSE);
} catch (NoSuchAlgorithmException e) {
LOGGER.error("Random_Key_Generate_OTP_Error");
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Stack_Message_Error", e);
}
}
return randomKey;
}
/**
* Generate TOTP(Time-based One-time Password) code
* 生成基于时间的一次性密码
*
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param fixedTime Client fixed time
* 客户端的修正时间
* @param syncCount Time Step, Unit: Second
* 时间步长,单位:秒
* @param fixWindow Fix window size
* 最多可偏移时间
*
* @return Generated code
* 生成的一次性密码
*/
private static int generateTOTPCode(final CalcType calcType, final String secret, final long fixedTime,
final int syncCount, final int fixWindow) {
long currentTime = DateTimeUtils.currentTimeMillis();
long calcTime = (currentTime + fixedTime) / 1000L;
if (syncCount > 0) {
calcTime /= syncCount;
} else {
calcTime /= DEFAULT_SYNC_COUNT;
}
calcTime += fixWindow;
return generateCode(calcType, secret, calcTime);
}
/**
* Generate OTP(One-time Password) code
* 生成基于时间的一次性密码
*
* @param calcType Calculate type
* 密码算法类型
* @param secret Secret key string
* 随机密钥字符串
* @param randomCode Random number
* 随机数
*
* @return Generated code
* 生成的一次性密码
*/
private static int generateCode(final CalcType calcType, final String secret, long randomCode) {
byte[] signData = new byte[8];
try {
RawUtils.writeLong(signData, randomCode);
} catch (DataInvalidException e) {
LOGGER.error("Process_Signature_Data_OTP_Error");
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Stack_Message_Error", e);
}
return Globals.DEFAULT_VALUE_INT;
}
byte[] secretBytes = StringUtils.base32Decode(secret);
byte[] hash;
switch (calcType) {
case HmacSHA1:
hash = SecurityUtils.HmacSHA1(secretBytes, signData);
break;
case HmacSHA256:
hash = SecurityUtils.HmacSHA256(secretBytes, signData);
break;
case HmacSHA512:
hash = SecurityUtils.HmacSHA512(secretBytes, signData);
break;
default:
return Globals.DEFAULT_VALUE_INT;
}
int offset = hash[hash.length - 1] & 0xF;
long resultCode = 0L;
for (int i = 0 ; i < 4 ; ++i) {
resultCode = (resultCode << 8) | (hash[offset + i] & 0xFF);
}
resultCode &= 0x7FFFFFFF;
resultCode %= 1000000;
return (int)resultCode;
}
/**
* Enumeration of Calculate type
* 密码算法类型枚举
*/
public enum CalcType {
HmacSHA1, HmacSHA256, HmacSHA512
}
}