1. 什么是OTP

一次性密码(One Time Password,简称OTP),又称“一次性口令”,是指只能使用一次的密码。

1

2. OTP原理

动态密码的产生方式,主要是以时间差做为服务器与密码产生器的同步条件。在需要登录的时候,就利用密码产生器产生动态密码,OTP一般分为计次使用以及计时使用两种,计次使用的OTP产出后,可在不限时间内使用;计时使用的OTP则可设置密码有效时间,从30秒到两分钟不等,而OTP在进行认证之后即废弃不用,下次认证必须使用新的密码,增加了试图不经授权访问有限制资源的难度。

计算公式:

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

3.Java实现全代码TOTP

该代码可以直接运行

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import java.lang.reflect.UndeclaredThrowableException;

import java.math.BigInteger;

import java.security.GeneralSecurityException;

import java.util.Date;

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 = 5000;

/**

* 转码位数 [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();

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

该OTP运行后,得到的是可以校验是否有效的口令,但是无法判断这个口令属于哪一个用户,像付款码,当使用人数不断增多时,单纯该数字无法确认哪一个人的付款码,则现在需要在这串数据中,融入用户ID,其计算公式可以:

4、付款码计算与提取

付款码=TOTP * 质数 + 用户ID

质数取值需要大于最大用户ID,如:当前系统设计最大承受用户有9900人,则质数可以设定必须大于9900的数值(9991).

1

2

提起用户ID

用户ID = 付款码 % 质数

“%”表示取余

1

2

提取TOTP

TOTP=付款码 / 质数

当付款码除质数后取整,则该数值为当前付款码中的OTP。

1

2

当服务器获取到付款码时:

1、首先提取TOTP,判断该口令是否有效,如果校验失败,则可以接口返回,付款码错误。

2、第一步校验通过后,查找缓存,判断在规定的一段时间内是否有使用过该付款码,并更新缓存,把该次记录写到缓存中。

3、第一步和第二步通过后,获取付款码中用户ID,将用户ID进行扣款操作。

4、服务器继续走剩下流程。

————————————————

原文链接:https://blog.csdn.net/qq_43655984/java/article/details/105598603

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=112

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=113

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=114

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=115

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=116

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=81

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=82

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=83

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=84

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=85

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=86

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=88

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=89

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=90

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=91

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=92

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=78

https://jlwz.cn/album/albumlist.aspx?siteid=1000&classid=0&smalltypeid=79

什么是OTP:Java一次动态密码、付款码原理

原文:https://www.cnblogs.com/dasdfdfecvcx/p/12763569.html

java 动态密码错误_什么是OTP:Java一次动态密码、付款码原理相关推荐

  1. 第一个java程序的错误_我是一名java初学者,执行第一个java程序welcome.java出现了以下错误,这是为什么?...

    我是一名java初学者,执行第一个java程序welcome.java出现了以下错误,这是为什么? welcome.java: import javax.swing.*; public class w ...

  2. java xml出错,Java xml出现错误 javax.xml.transform.TransformerException: java.lang.NullPointerException...

    Java xml出现错误 javax.xml.transform.TransformerException: java.lang.NullPointerException解决办法: 利用Java操作X ...

  3. Java xml出现错误 javax.xml.transform.TransformerException: java.lang.NullPointerException

    转自:https://www.jb51.net/article/98644.htm Java xml出现错误 javax.xml.transform.TransformerException: jav ...

  4. OTP:Java一次动态密码、付款码原理

    1. 什么是OTP 一次性密码(One Time Password,简称OTP),又称"一次性口令",是指只能使用一次的密码. 2. OTP原理 动态密码的产生方式,主要是以时间差 ...

  5. java @valid 密码不一致_一个成熟的Java项目如何优雅地处理异常

    (一)概述 异常处理是一个系统最重要的环节,当一个项目变得很大的时候,异常处理和日志系统能让你快速定位到问题.对于用户或者接口调用者而言,优雅的异常处理可以让调用者快速知道问题所在.本文将介绍如何优雅 ...

  6. java实验常见错误_求高手点拨一个Java SWING的IM系统实验的错误解决方案。

    高手,您好: 小弟遇到的代码场景为: 制作了一个Java SWING技术结合Socket技术实现的IM通信实验. 通信为双向全双工通信,通信方分为S端和C端. 下面,小弟贴出小弟的全部实验源代码,请高 ...

  7. 我的世java途径错误_我的世界JAVA路径错误的解决方法分享

    我的世界JAVA路径错误该怎么解决呢?JAVA是Minecraft运行的虚拟环境必要条件,需要您安装后才开始游戏.很多童鞋在安装过程中都遇到了JAVA路径错误的提示,这是什么原因导致的?第一手游网必须 ...

  8. java date传输类型错误_转换日期格式:Java中的转换错误?

    我正在尝试将此日期转换为其他格式.不幸的是,他们成功地解析了日期并正确地保留了所有信息. 06-Dec-2017 07:14:56.656PM 至 2017-12-06 19:14:56.656 如果 ...

  9. java内存不足错误_调试Java内存不足错误

    我仍然是一个相对较新的程序员,我在 Java中遇到的一个问题是Out of Memory Errors.我不想使用-Xmx来增加内存,因为我觉得错误是由于编程不好造成的,我想改进编码而不是依靠更多的内 ...

最新文章

  1. linux ubuntu gcc编译 fatal error: bits/libc-header-start.h 错误解决
  2. redis的五种数据结构及其使用场景
  3. 集成电路史上著名的十个人,有几个中国人?
  4. LeetCode 2196. 根据描述创建二叉树(哈希)
  5. 一个好的技术团队应该怎么选择开发语言
  6. 【转载】指导教师的局域网聊天
  7. JavaScript中常见的字符串操作函数及用法汇总
  8. 我是如何获取新知识的?
  9. linux 查看是否有led设备,linux驱动开发--字符设备:通过cdd_cdev结构中的led变量区分是哪个节点,private_data使用...
  10. unix 网络编程全解
  11. tomcat日志配置(停止日志或修改日志路径)
  12. HDMI/DVI分配器芯片
  13. PHP格式化 插件 vs code
  14. 面试珠玑 嵌入式C程序员经典笔试题一
  15. H5端嵌入公众号后,修改头部标题
  16. Complex Multiplier IP 使用教程(源码)
  17. 区县公安合成作战中心调研:(一)引言
  18. 牛客算法周周练11 A.切题之路 签到题
  19. 硝酸浓度对多孔氧化锌薄膜刻蚀工艺能的影响
  20. 蚂蚁回应支付宝高新企业认证被撤销;谷歌嘲讽iPhone14:Pixel手机是创新上的领导者,苹果紧随其后|极客头条

热门文章

  1. 无人驾驶及Apollo开源平台技术教程
  2. 区块链技术相关知识笔记
  3. Java 创建并用应用幻灯片母版
  4. Ubuntu下软件安装的方法(配图解)
  5. Message中obtain()与recycle()
  6. 如何在iPad上使用VScode
  7. idea中用git管理文件之后文件颜色的含义
  8. 鲁宾逊微积分与“知识共享”,携手相伴进入中国
  9. python matlab 普朗克公式黑体光谱辐射出射度 绘图
  10. vue2.0,3.0官网