/**
Copyright (c) 2011 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, is permitted pursuant to, and subject to the license
terms contained in, the Simplified BSD License set forth in Section
4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
*/
import java.lang.reflect.UndeclaredThrowableException;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.util.TimeZone;
/**
* Dies ist eine Beispielimplementierung des OATH
* TOTP-Algorithmus.
* Besuchen Sie www.openauthentication.org für weitere Informationen.
*
* @author Johan Rydell, PortWise, Inc.
*/
public class TOTP {
private TOTP() {}
/**
* Diese Methode verwendet JCE, um den Kryptographie-Algorithmus bereitzustellen.
* HMAC berechnet einen Hash-basierten Message Authentication Code mit dem
* Krypto-Hash-Algorithmus als Parameter.
*
* @param crypto: der Krypto-Algorithmus (HmacSHA1, HmacSHA256,
* HmacSHA512)
* @param keyBytes: die Bytes für den HMAC-Schlüssel
* @param text: die zu authentifizierende Nachricht oder Text
*/
private static byte[] hmac_sha(String crypto, byte[] keyBytes,
byte[] text){
try {
Mac hmac;
hmac = Mac.getInstance(crypto);
SecretKeySpec macKey =
new SecretKeySpec(keyBytes, "RAW");
hmac.init(macKey);
return hmac.doFinal(text);
} catch (GeneralSecurityException gse) {
throw new UndeclaredThrowableException(gse);
}
}
/**
* Diese Methode konvertiert einen HEX-String in Byte[]
*
* @param hex: der HEX-String
*
* @return: ein Byte-Array
*/
private static byte[] hexStr2Bytes(String hex){
byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
byte[] ret = new byte[bArray.length - 1];
for (int i = 0; i < ret.length; i++)
ret[i] = bArray[i+1];
return ret;
}
private static final int[] DIGITS_POWER
= {1,10,100,1000,10000,100000,1000000,10000000,100000000 };
/**
* Diese Methode generiert einen TOTP-Wert für die gegebenen
* Parameter.
*
* @param key: das gemeinsame Geheimnis, HEX-kodiert
* @param time: ein Wert, der eine Zeit widerspiegelt
* @param returnDigits: Anzahl der zurückzugebenden Ziffern
*
* @return: ein numerischer String in Basis 10
*/
public static String generateTOTP(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA1");
}
/**
* Diese Methode generiert einen TOTP-Wert für die gegebenen
* Parameter.
*
* @param key: das gemeinsame Geheimnis, HEX-kodiert
* @param time: ein Wert, der eine Zeit widerspiegelt
* @param returnDigits: Anzahl der zurückzugebenden Ziffern
*
* @return: ein numerischer String in Basis 10
*/
public static String generateTOTP256(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA256");
}
/**
* Diese Methode generiert einen TOTP-Wert für die gegebenen
* Parameter.
*
* @param key: das gemeinsame Geheimnis, HEX-kodiert
* @param time: ein Wert, der eine Zeit widerspiegelt
* @param returnDigits: Anzahl der zurückzugebenden Ziffern
*
* @return: ein numerischer String in Basis 10
*/
public static String generateTOTP512(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA512");
}
/**
* Diese Methode generiert einen TOTP-Wert für die gegebenen
* Parameter.
*
* @param key: das gemeinsame Geheimnis, HEX-kodiert
* @param time: ein Wert, der eine Zeit widerspiegelt
* @param returnDigits: Anzahl der zurückzugebenden Ziffern
* @param crypto: die zu verwendende Krypto-Funktion
*
* @return: ein numerischer String in Basis 10
*/
public static String generateTOTP(String key,
String time,
String returnDigits,
String crypto){
int codeDigits = Integer.decode(returnDigits).intValue();
String result = null;
while (time.length() < 16 )
time = "0" + time;
byte[] msg = hexStr2Bytes(time);
byte[] k = hexStr2Bytes(key);
byte[] hash = hmac_sha(crypto, k, msg);
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[codeDigits];
result = Integer.toString(otp);
while (result.length() < codeDigits) {
result = "0" + result;
}
return result;
}
public static void main(String[] args) {
String seed = "3132333435363738393031323334353637383930";
String seed32 = "3132333435363738393031323334353637383930" +
"313233343536373839303132";
String seed64 = "3132333435363738393031323334353637383930" +
"3132333435363738393031323334353637383930" +
"3132333435363738393031323334353637383930" +
"31323334";
long T0 = 0;
long X = 30;
long testTime[] = {59L, 1111111109L, 1111111111L,
1234567890L, 2000000000L, 20000000000L};
String steps = "0";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
System.out.println(
"+---------------+-----------------------+" +
"------------------+--------+--------+");
System.out.println(
"| Time(sec) | Time (UTC format) " +
"| Value of T(Hex) | TOTP | Mode |");
System.out.println(
"+---------------+-----------------------+" +
"------------------+--------+--------+");
for (int i=0; i<testTime.length; i++) {
long T = (testTime[i] - T0)/X;
steps = Long.toHexString(T).toUpperCase();
while (steps.length() < 16) steps = "0" + steps;
String fmtTime = String.format("%1$-11s", testTime[i]);
String utcTime = df.format(new Date(testTime[i]*1000));
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed, steps, "8",
"HmacSHA1") + "| SHA1 |");
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed32, steps, "8",
"HmacSHA256") + "| SHA256 |");
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed64, steps, "8",
"HmacSHA512") + "| SHA512 |");
System.out.println(
"+---------------+-----------------------+" +
"------------------+--------+--------+");
}
}catch (final Exception e){
System.out.println("Error : " + e);
}
}
}