What is two-factor authentication?
Popular speaking, the general authentication are username / password way, this is only a factor for password authentication, two-factor increase is nothing more than a factor authentication to enhance security.
Common Solutions
- SMS
- Phone by voice
- TOTP Solutions
The first three programs, they were all similar. Server side generated by an algorithm for a random password, delivered to the user via SMS, e-mail or telephone way, random password to the user after Server, Server verified, completed a two-factor authentication as the logon credentials. However, text messages and telephone voice for the operating companies there is a certain cost, in addition to some non-Internet applications may not be through the public network. In this case, TOTP be a good two-factor authentication solutions.
What is TOTP?
It is Time-based One-Time Password shorthand represents a one-time password algorithms based on the timestamp.
If we played Fantasy Westward Journey, then on 将军令
should not be unfamiliar, this is a product based on TOTP.
OTP
Before the introduction TOTP, first introducedOTP
One-Time Password shorthand represents a one-time password.
OTP(K,C) = Truncate(HMAC-SHA-1(K,C))
Wherein, K representative of key string; C is a number representing a random number; HMAC-SHA-1 represented by do HMAC SHA-1;
Truncate is a function, for the encrypted string is taken, some of the fields and taking up a string encrypted digital.
Encrypted HMAC-SHA-1 mode is, Truncate to achieve the following:
- HMAC-SHA-1 to give a length of the encrypted 20-byte encrypted string;
- Take the last byte of the 20-byte encrypted string, which takes the lower 4 bits of the byte, taken as the standard offset encrypted string;
- Start offset index according to obtain 4 bytes, big endian (high byte in the low-order address) is composed of an integer embodiment;
- After intercepts the integer 6 or 8 converted into a string.
public static String generateOTP(String K,
String C,
String returnDigits,
String crypto){
int codeDigits = Integer.decode(returnDigits).intValue();
String result = null;
// K是密码
// C是产生的随机数
// crypto是加密算法 HMAC-SHA-1
byte[] hash = hmac_sha(crypto, K, C);
// hash为20字节的字符串
// put selected bytes into result int
// 获取hash最后一个字节的低4位,作为选择结果的开始下标偏移
int offset = hash[hash.length - 1] & 0xf;
// 获取4个字节组成一个整数,其中第一个字节最高位为符号位,不获取,使用0x7f
int binary =
((hash[offset] & 0x7f) << 24) |
((hash[offset + 1] & 0xff) << 16) |
((hash[offset + 2] & 0xff) << 8) |
(hash[offset + 3] & 0xff);
// 获取这个整数的后6位(可以根据需要取后8位)
int otp = binary % 1000000;
// 将数字转成字符串,不够6位前面补0
result = Integer.toString(otp);
while (result.length() < codeDigits) {
result = "0" + result;
}
return result;
}
The results returned is to see a number of dynamic password.
HOTP
We know the basic principles of the OTP, HOTP just one of the parameters C into a random number
Modify the formula
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
HOTP: Generates the OTP for the given count
Namely: C as an argument, to obtain dynamic password.
General HOTP predetermined hash function using SHA2, namely: do event based on SHA-256 or SHA-512 [SHA2] synchronization verification hash function;
Detailed TOTP
TOTP just where parameter C into the number generated by the time stamp.
TOTP(K,C) = HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
TOTP is different from the timestamp calculated C.
C = (T - T0) / X;
T represents the current Unix timestamp
T0 general value to zero.
X represents the number of time steps, that is to say more than a dynamic password is generated for a long time, this time interval is the time X number of steps, the system default is 30 seconds;
E.g:
T0 = 0;
X = 30;
T = 30 ~ 59, C = 1; 30 to 59 represents a dynamic password which coincides 30 seconds.
T = 60 ~ 89, C = 2; 30 to 59 represents a dynamic password which coincides 30 seconds.
Different manufacturers use different time steps of;
- Number of time steps Alibaba treasure identity used is 60 seconds;
- Number of time steps using the token rather shield is 60 seconds;
- Google's Authenticator number of time steps is 30 seconds;
- Token Tencent number of time steps is 60 seconds;
application
There are many clients achieve above have been listed. The server-side implementation library is relatively small, seemingly are also achieved unofficial. Here we recommend a JAVA implementation library , which is a private library, a friend of mind only their own line and the wheel.
Here library based on the realization, some demo code is given for reference purposes only.
package com.github.chenqimiao.util;
import java.text.MessageFormat;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
import com.warrenstrange.googleauth.GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/**
* @Auther: chenqimiao
* @Date: 2019/8/26 22:58
* @Description: refer https://github.com/wstrange/GoogleAuth
*/
@Slf4j
public class GoogleAuthenticatorUtils {
// 前缀
private static final String DEFAULT_USER_PREFIX = "TOTP_USER:";
// 用户名|密钥|发行者
public static final String QRCODE_TEMPLATE = "otpauth://totp/" + DEFAULT_USER_PREFIX + "{0}?secret={1}&issuer={2}";
// 默认的发行者
public static final String DEFAULT_ISSUER = "DAS_TOTP";
private static final GoogleAuthenticatorConfig DEFAULT_CONFIG;
static {
GoogleAuthenticatorConfigBuilder builder = new GoogleAuthenticatorConfigBuilder();
// Do something here if you want to set config for GoogleAuthenticator
DEFAULT_CONFIG = builder.build();
}
public static String createQrCodeContent(String username, String secret) {
return createQrCodeContent(username, secret, DEFAULT_ISSUER);
}
public static String createQrCodeContent(String username, String secret, String issuer) {
return MessageFormat.format(QRCODE_TEMPLATE, username, secret, issuer);
}
public static String createSecret() {
return createSecret(DEFAULT_CONFIG);
}
public static String createSecret(GoogleAuthenticatorConfig config) {
GoogleAuthenticator gAuth = new GoogleAuthenticator(config);
final GoogleAuthenticatorKey key = gAuth.createCredentials();
return key.getKey();
}
public static boolean verify(Integer totpPwd, String secret) {
return verify(totpPwd, secret, DEFAULT_CONFIG);
}
public static boolean verify(Integer totpPwd, String secret, GoogleAuthenticatorConfig config) {
GoogleAuthenticator gAuth = new GoogleAuthenticator(config);
return gAuth.authorize(secret, totpPwd);
}
public static Integer getTotpPassword(String secret) {
return getTotpPassword(secret, DEFAULT_CONFIG);
}
public static Integer getTotpPassword(String secret, GoogleAuthenticatorConfig config) {
GoogleAuthenticator gAuth = new GoogleAuthenticator(config);
return gAuth.getTotpPassword(secret);
}
@SneakyThrows
public static void main(String args[]) {
String secret = createSecret();
String qrcodeContent = createQrCodeContent("chenqimiao", secret);
System.out.println("qrcodeContent is " + qrcodeContent);
Integer totpPwd = getTotpPassword(secret);
System.out.println("Current totp password is " + totpPwd);
boolean result = verify(totpPwd, secret);
System.out.println("result is " + result);
}
qrcodeContent two-dimensional code can be generated by two-dimensional code tool, using the Google Authenticator After scanning the two-dimensional code, it is equivalent to a user bound authenticator.