/**
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;
/**
* Questa è un'implementazione di esempio dell'algoritmo
* TOTP di OATH.
* Visita www.openauthentication.org per ulteriori informazioni.
*
* @author Johan Rydell, PortWise, Inc.
*/
public class TOTP {
private TOTP() {}
/**
* Questo metodo utilizza JCE per fornire l'algoritmo crittografico.
* HMAC calcola un codice di autenticazione del messaggio hash con
* l'algoritmo di hash crittografico come parametro.
*
* @param crypto: l'algoritmo crittografico (HmacSHA1, HmacSHA256,
* HmacSHA512)
* @param keyBytes: i byte da utilizzare per la chiave HMAC
* @param text: il messaggio o il testo da autenticare
*/
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);
}
}
/**
* Questo metodo converte una stringa HEX in Byte[]
*
* @param hex: la stringa HEX
*
* @return: un array di byte
*/
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 };
/**
* Questo metodo genera un valore TOTP per il set
* di parametri fornito.
*
* @param key: il segreto condiviso, codificato HEX
* @param time: un valore che riflette un tempo
* @param returnDigits: numero di cifre da restituire
*
* @return: una stringa numerica in base 10
*/
public static String generateTOTP(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA1");
}
/**
* Questo metodo genera un valore TOTP per il set
* di parametri fornito.
*
* @param key: il segreto condiviso, codificato HEX
* @param time: un valore che riflette un tempo
* @param returnDigits: numero di cifre da restituire
*
* @return: una stringa numerica in base 10
*/
public static String generateTOTP256(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA256");
}
/**
* Questo metodo genera un valore TOTP per il set
* di parametri fornito.
*
* @param key: il segreto condiviso, codificato HEX
* @param time: un valore che riflette un tempo
* @param returnDigits: numero di cifre da restituire
*
* @return: una stringa numerica in base 10
*/
public static String generateTOTP512(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA512");
}
/**
* Questo metodo genera un valore TOTP per il set
* di parametri fornito.
*
* @param key: il segreto condiviso, codificato HEX
* @param time: un valore che riflette un tempo
* @param returnDigits: numero di cifre da restituire
* @param crypto: la funzione crittografica da utilizzare
*
* @return: una stringa numerica in base 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);
}
}
}