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

bea8c5a55e65a200c3aa5024febca573.png

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值