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 |
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* https://tools.ietf.org/html/rfc6238
* https://weblogs.java.net/blog/evanx/archive/2012/11/07/google-authenticator-thus-enabled
*/
public class TOTP {
static String encodedKey;
public static String getQRBarcodeURL(String user, String host, String secret) {
String format = "https://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s%%3Fsecret%%3D%s";
return String.format(format, user, host, secret);
}
public static void main(String[] args) throws Exception {
byte[] secretKey = new byte[10];
new SecureRandom().nextBytes(secretKey);
encodedKey = Base32.encodeRfc(secretKey);
System.out.println("secret => " + encodedKey);
System.out.println(getQRBarcodeURL("jo.27", "acekorea.info", encodedKey));
long X = 30;
long T0 = 0;
long t = (System.currentTimeMillis() - T0) / 1000 / X;
System.out.println(getCode(encodedKey, t));
System.out.println(getCodeList(encodedKey, t, 5));
}
public static boolean verifyCode(String secret, int code, long timeIndex,
int variance) throws Exception {
byte[] secretBytes = Base32.decodeRfc(secret);
for (int i = -variance; i <= variance; i++) {
if (getCode(secretBytes, timeIndex + i) == code) {
return true;
}
}
return false;
}
private static long getCode(byte[] secret, long timeIndex) throws Exception {
SecretKeySpec signKey = new SecretKeySpec(secret, "HmacSHA1");
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.putLong(timeIndex);
byte[] timeBytes = buffer.array();
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signKey);
byte[] hash = mac.doFinal(timeBytes);
int offset = hash[19] & 0xf;
long truncatedHash = hash[offset] & 0x7f;
for (int i = 1; i < 4; i++) {
truncatedHash <<= 8;
truncatedHash |= hash[offset + i] & 0xff;
}
return (truncatedHash %= 1000000);
}
static long getCode(String secret, long timeIndex) throws Exception {
return getCode(Base32.decodeRfc(secret), timeIndex);
}
static List getCodeList(String secret, long timeIndex, int variance) throws Exception {
List list = new ArrayList();
for (int i = -variance; i <= variance; i++) {
list.add(getCode(secret, timeIndex + i));
}
return list;
}
} |