http://www.slideshare.net/markstanislav/twofactor-authentication-a-primer
public class TOTP {
private static final int[] DIGITS_POWER
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
private static byte[] hmacSha(String crypto, byte[] keyBytes, byte[] text) {
try {
Mac hmac = Mac.getInstance(crypto);
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmac.init(macKey);
return hmac.doFinal(text);
} catch (GeneralSecurityException gse) {
throw new UndeclaredThrowableException(gse);
}
}
public static int generateTOTP(byte[] key, long time, int digits, String crypto) {
byte[] msg = ByteBuffer.allocate(8).putLong(time).array();
byte[] hash = hmacSha(crypto, key, msg);
// put selected bytes into result int
int offset = hash[hash.length - 1] & 0xf;
int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
int otp = binary % DIGITS_POWER[digits];
return otp;
}
}
public class TOTPUtils {
private int SECRET_SIZE = 10;
private int PASS_CODE_LENGTH = 6;
private int WINDOW = 3;
private int INTERVAL = 30;
private String CRYPTO = "HmacSHA1";
public String generateSecret() {
// Allocating the buffer
byte[] buffer = new byte[SECRET_SIZE];
// Filling the buffer with random numbers.
rand.nextBytes(buffer);
byte[] secretKey = Arrays.copyOf(buffer, SECRET_SIZE);
byte[] encodedKey = BaseEncoding.base32().encode(secretKey).getBytes();
return new String(encodedKey);
}
public boolean checkCode(String secret, long code) throws NoSuchAlgorithmException, InvalidKeyException {
byte[] decodedKey = BaseEncoding.base32().decode(secret);
// Window is used to check codes generated in the near past.
int window = WINDOW;
long currentInterval = getCurrentInterval();
for (int i = -window; i <= window; ++i) {
long hash = TOTP.generateTOTP(decodedKey, currentInterval + i, PASS_CODE_LENGTH, CRYPTO);
if (hash == code) {
return true;
}
}
return false;
}
}
public class TwoFactorCodeManager {
@Autowired
private TwoFactorCodeRepo repo;
@Autowired
private TOTPUtils totpUtils;
public boolean hasLink(String userId) {
return repo.hasLink(userId);
}
public String generatorTotpCode(String userId) {
String totpCode = totpUtils.generateSecret();
return totpCode;
}
public boolean linkCode(String totpCode, long inputCode, String userId) throws InvalidKeyException, NoSuchAlgorithmException {
boolean verifySuccess = totpUtils.checkCode(totpCode, inputCode);
if (verifySuccess) {
repo.updateTotpCode(totpCode, userId);
}
return verifySuccess;
}
public boolean verifyCode(long inputCode, String userId) throws InvalidKeyException, NoSuchAlgorithmException {
String totpCode = repo.getTotpCode(userId);
return totpUtils.checkCode(totpCode, inputCode);
}
}