OtpUtils.java 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package org.springblade.common.utils;
  2. import cn.hutool.core.lang.Assert;
  3. import org.springblade.common.enums.ResCode;
  4. import org.springblade.core.log.exception.ServiceException;
  5. import javax.crypto.Mac;
  6. import javax.crypto.spec.SecretKeySpec;
  7. import java.lang.reflect.UndeclaredThrowableException;
  8. import java.math.BigInteger;
  9. import java.security.GeneralSecurityException;
  10. import java.util.Date;
  11. /**
  12. * @author: lianghanqiang
  13. * @description: TOP 一次性加密口令
  14. * @since: 8/2/21 -- 10:48 AM
  15. */
  16. public class OtpUtils {
  17. // 649957
  18. public static void main(String[] args) {
  19. System.out.println(generateMyTOTP("1423159010830884865"));
  20. }
  21. /**
  22. * id变形常量
  23. * */
  24. private static final Long TRANSFORM_PARAMS = 95963L;
  25. /**
  26. * 共享密钥
  27. */
  28. private static final String SECRET_KEY = "";
  29. /**
  30. * 时间步长 单位:毫秒 作为口令变化的时间周期
  31. */
  32. private static final long STEP = 60000;
  33. /**
  34. * 转码位数 [1-8]
  35. */
  36. private static final int CODE_DIGITS = 6;
  37. /**
  38. * 初始化时间
  39. */
  40. private static final long INITIAL_TIME = 0;
  41. /**
  42. * 数子量级
  43. */
  44. private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
  45. private OtpUtils() {
  46. }
  47. /**
  48. * 生成一次性密码
  49. *
  50. * @param id
  51. * @return String
  52. */
  53. public static String generateMyTOTP(String id) {
  54. Assert.notBlank(id,()-> new ServiceException(ResCode.ID_NOT_NULL));
  55. long now = new Date().getTime();
  56. String time = Long.toHexString(timeFactor(now)).toUpperCase();
  57. return generateTOTP(id + SECRET_KEY, time);
  58. }
  59. /**
  60. * 获取动态因子
  61. *
  62. * @param targetTime 指定时间
  63. * @return long
  64. */
  65. private static long timeFactor(long targetTime) {
  66. return (targetTime - INITIAL_TIME) / STEP;
  67. }
  68. /**
  69. * 哈希加密
  70. *
  71. * @param crypto 加密算法
  72. * @param keyBytes 密钥数组
  73. * @param text 加密内容
  74. * @return byte[]
  75. */
  76. private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
  77. try {
  78. Mac hmac;
  79. hmac = Mac.getInstance(crypto);
  80. SecretKeySpec macKey = new SecretKeySpec(keyBytes, "AES");
  81. hmac.init(macKey);
  82. return hmac.doFinal(text);
  83. } catch (GeneralSecurityException gse) {
  84. throw new UndeclaredThrowableException(gse);
  85. }
  86. }
  87. private static byte[] hexStr2Bytes(String hex) {
  88. byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
  89. byte[] ret = new byte[bArray.length - 1];
  90. System.arraycopy(bArray, 1, ret, 0, ret.length);
  91. return ret;
  92. }
  93. private static String generateTOTP(String key, String time) {
  94. return generateTOTP(key, time, "HmacSHA1");
  95. }
  96. private static String generateTOTP(String key, String time, String crypto) {
  97. StringBuilder timeBuilder = new StringBuilder(time);
  98. while (timeBuilder.length() < 16)
  99. timeBuilder.insert(0, "0");
  100. time = timeBuilder.toString();
  101. byte[] msg = hexStr2Bytes(time);
  102. byte[] k = key.getBytes();
  103. // byte[] k = Base32.decode(key);
  104. byte[] hash = hmac_sha(crypto, k, msg);
  105. return truncate(hash);
  106. }
  107. /**
  108. * 截断函数
  109. *
  110. * @param target 20字节的字符串
  111. * @return String
  112. */
  113. private static String truncate(byte[] target) {
  114. StringBuilder result;
  115. int offset = target[target.length - 1] & 0xf;
  116. int binary = ((target[offset] & 0x7f) << 24)
  117. | ((target[offset + 1] & 0xff) << 16)
  118. | ((target[offset + 2] & 0xff) << 8) | (target[offset + 3] & 0xff);
  119. int otp = binary % DIGITS_POWER[CODE_DIGITS];
  120. result = new StringBuilder(Integer.toString(otp));
  121. while (result.length() < CODE_DIGITS) {
  122. result.insert(0, "0");
  123. }
  124. return result.toString();
  125. }
  126. /**
  127. * 校验口令
  128. * */
  129. public static boolean validate(String id,String code) {
  130. String s = generateMyTOTP(id);
  131. return code.equals((Long.valueOf(id))+s);
  132. }
  133. /**
  134. * 根据code获取用户Id
  135. * */
  136. public static String getIdFormCode(String code) {
  137. return Long.valueOf(code.substring(0,19))+"";
  138. }
  139. }