public static class GoogleAuthenticator {
public static String generateSecret() {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[32];
random.nextBytes(bytes);
Base32 codec = new Base32();
byte[] secretKey = Arrays.copyOf(bytes, GoogleAuthentictorSecretSize);
byte[] bEncodedKey = codec.encode(secretKey);
return new String(bEncodedKey);
}
public static String getQRBarcodeURL(String user, String host, String secret) {
return "otpauth://totp/"+user+"@"+host+"?secret="+secret;
}
private static int verify_code(byte[] key,long t) throws NoSuchAlgorithmException, InvalidKeyException {
byte[] data = new byte[8];
long value = t;
for (int i = 8; i-- > 0; value >>>= 8) {
data[i] = (byte) value;
}
SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signKey);
byte[] hash = mac.doFinal(data);
int offset = hash[20 - 1] & 0xF;
// We're using a long because Java hasn't got unsigned int.
long truncatedHash = 0;
for (int i = 0; i < 4; ++i) {
truncatedHash <<= 8;
// We are dealing with signed bytes:
// we just keep the first byte.
truncatedHash |= (hash[offset + i] & 0xFF);
}
truncatedHash &= 0x7FFFFFFF;
truncatedHash %= 1000000;
return (int) truncatedHash;
}
public static boolean check_code(String secret, long code, long t) throws NoSuchAlgorithmException, InvalidKeyException {
Base32 codec = new Base32();
byte[] decodedKey = codec.decode(secret);
// Window is used to check codes generated in the near past.
// You can use this value to tune how far you're willing to go.
int window = 0;
for (int i = -window; i <= window; ++i) {
long hash = verify_code(decodedKey, t + i);
if (hash == code) {
return true;
}
}
// The validation code is invalid.
return false;
}
}