200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > java 手机动态口令_动态密码TOTP的Java实现

java 手机动态口令_动态密码TOTP的Java实现

时间:2023-05-05 18:51:46

相关推荐

java 手机动态口令_动态密码TOTP的Java实现

一、HOTP

HOTP 算法,全称是“An HMAC-Based One-Time Password Algorithm”,是一种基于事件计数的一次性密码生成算法,详细的算法介绍可以查看 RFC 4226。其实算法本身非常简单,算法本身可以用两条简短的表达式描述:

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

PWD(K,C,digit) = HOTP(K,C) mod 10^Digit

二、TOTP

TOTP 算法,全称是 TOTP: Time-Based One-Time Password Algorithm,其基于 HOTP 算法实现,核心是将移动因子从 HOTP 中的事件计数改为时间差。完整的 TOTP 算法的说明可以查看 RFC 6238,其公式描述也非常简单:

TOTP = HOTP(K, T) // T is an integer

and represents the number of time steps between the initial counter

time T0 and the current Unix time

More specifically, T = (Current Unix time - T0) / X, where the

default floor function is used in the computation.

参考《/a/1190000008394200》

这篇文章已经介绍清楚动态密码的原理,本文仅在理论的基础上使用Java实现。

三、TOTP具体Java实现

子服务端:启用不含verifyTOTP*()验证方法的TOTP,使用子服务端的账户和密码加密,向验证服务端发送动态口令。

验证服务端:验证子服务端的口令,根据子服务端标识信息,使用其账户和密码加密得到口令,比对口令是否一致即可。

前提:因为使用时间作为动态因子加密口令,子服务端的时间应和验证服务端的时间一致,比如使用同一个授时服务器授时。

推荐使用柔性口令验证,使用柔性验证时请设置时间回溯参数,避免因口令在网络中传输消耗时间,或者服务端时间误差导致口令失效。

/**

*

ClassName: TOTP

*

Description: TOTP = HOTP(K, T) // T is an integer

* and represents the number of time steps between the initial counter

* time T0 and the current Unix time

*

* More specifically, T = (Current Unix time - T0) / X, where the

* default floor function is used in the computation.

*

* @author wangqian

* @date -04-03 11:44

*/

public class TOTP {

public static void main(String[] args) {

try {

for (int j = 0; j < 10; j++) {

String totp = generateMyTOTP("account01", "12345");

System.out.println(String.format("加密后: %s", totp));

Thread.sleep(1000);

}

} catch (final Exception e) {

e.printStackTrace();

}

}

/**

* 共享密钥

*/

private static final String SECRET_KEY = "ga35sdia43dhqj6k3f0la";

/**

* 时间步长 单位:毫秒 作为口令变化的时间周期

*/

private static final long STEP = 30000;

/**

* 转码位数 [1-8]

*/

private static final int CODE_DIGITS = 8;

/**

* 初始化时间

*/

private static final long INITIAL_TIME = 0;

/**

* 柔性时间回溯

*/

private static final long FLEXIBILIT_TIME = 5000;

/**

* 数子量级

*/

private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};

private TOTP() {

}

/**

* 生成一次性密码

*

* @param code 账户

* @param pass 密码

* @return String

*/

public static String generateMyTOTP(String code, String pass) {

if (EmptyUtil.isEmpty(code) || EmptyUtil.isEmpty(pass)) {

throw new RuntimeException("账户密码不许为空");

}

long now = new Date().getTime();

String time = Long.toHexString(timeFactor(now)).toUpperCase();

return generateTOTP(code + pass + SECRET_KEY, time);

}

/**

* 刚性口令验证

*

* @param code 账户

* @param pass 密码

* @param totp 待验证的口令

* @return boolean

*/

public static boolean verifyTOTPRigidity(String code, String pass, String totp) {

return generateMyTOTP(code, pass).equals(totp);

}

/**

* 柔性口令验证

*

* @param code 账户

* @param pass 密码

* @param totp 待验证的口令

* @return boolean

*/

public static boolean verifyTOTPFlexibility(String code, String pass, String totp) {

long now = new Date().getTime();

String time = Long.toHexString(timeFactor(now)).toUpperCase();

String tempTotp = generateTOTP(code + pass + SECRET_KEY, time);

if (tempTotp.equals(totp)) {

return true;

}

String time2 = Long.toHexString(timeFactor(now - FLEXIBILIT_TIME)).toUpperCase();

String tempTotp2 = generateTOTP(code + pass + SECRET_KEY, time2);

return tempTotp2.equals(totp);

}

/**

* 获取动态因子

*

* @param targetTime 指定时间

* @return long

*/

private static long timeFactor(long targetTime) {

return (targetTime - INITIAL_TIME) / STEP;

}

/**

* 哈希加密

*

* @param crypto 加密算法

* @param keyBytes 密钥数组

* @param text 加密内容

* @return byte[]

*/

private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {

try {

Mac hmac;

hmac = Mac.getInstance(crypto);

SecretKeySpec macKey = new SecretKeySpec(keyBytes, "AES");

hmac.init(macKey);

return hmac.doFinal(text);

} catch (GeneralSecurityException gse) {

throw new UndeclaredThrowableException(gse);

}

}

private static byte[] hexStr2Bytes(String hex) {

byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();

byte[] ret = new byte[bArray.length - 1];

System.arraycopy(bArray, 1, ret, 0, ret.length);

return ret;

}

private static String generateTOTP(String key, String time) {

return generateTOTP(key, time, "HmacSHA1");

}

private static String generateTOTP256(String key, String time) {

return generateTOTP(key, time, "HmacSHA256");

}

private static String generateTOTP512(String key, String time) {

return generateTOTP(key, time, "HmacSHA512");

}

private static String generateTOTP(String key, String time, String crypto) {

StringBuilder timeBuilder = new StringBuilder(time);

while (timeBuilder.length() < 16)

timeBuilder.insert(0, "0");

time = timeBuilder.toString();

byte[] msg = hexStr2Bytes(time);

byte[] k = key.getBytes();

byte[] hash = hmac_sha(crypto, k, msg);

return truncate(hash);

}

/**

* 截断函数

*

* @param target 20字节的字符串

* @return String

*/

private static String truncate(byte[] target) {

StringBuilder result;

int offset = target[target.length - 1] & 0xf;

int binary = ((target[offset] & 0x7f) << 24)

| ((target[offset + 1] & 0xff) << 16)

| ((target[offset + 2] & 0xff) << 8) | (target[offset + 3] & 0xff);

int otp = binary % DIGITS_POWER[CODE_DIGITS];

result = new StringBuilder(Integer.toString(otp));

while (result.length() < CODE_DIGITS) {

result.insert(0, "0");

}

return result.toString();

}

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。