/**
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;
/**
* Ceci est un exemple d'implémentation de l'algorithme
* TOTP de l'OATH.
* Visitez www.openauthentication.org pour plus d'informations.
*
* @author Johan Rydell, PortWise, Inc.
*/
public class TOTP {
private TOTP() {}
/**
* Cette méthode utilise JCE pour fournir l'algorithme cryptographique.
* HMAC calcule un code d'authentification de message haché avec
* l'algorithme de hachage cryptographique comme paramètre.
*
* @param crypto: l'algorithme cryptographique (HmacSHA1, HmacSHA256,
* HmacSHA512)
* @param keyBytes: les octets à utiliser pour la clé HMAC
* @param text: le message ou le texte à authentifier
*/
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);
}
}
/**
* Cette méthode convertit une chaîne HEX en Byte[]
*
* @param hex: la chaîne HEX
*
* @return: un tableau d'octets
*/
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 };
/**
* Cette méthode génère une valeur TOTP pour l'ensemble
* de paramètres donné.
*
* @param key: le secret partagé, encodé en HEX
* @param time: une valeur qui reflète un temps
* @param returnDigits: nombre de chiffres à retourner
*
* @return: une chaîne numérique en base 10
*/
public static String generateTOTP(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA1");
}
/**
* Cette méthode génère une valeur TOTP pour l'ensemble
* de paramètres donné.
*
* @param key: le secret partagé, encodé en HEX
* @param time: une valeur qui reflète un temps
* @param returnDigits: nombre de chiffres à retourner
*
* @return: une chaîne numérique en base 10
*/
public static String generateTOTP256(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA256");
}
/**
* Cette méthode génère une valeur TOTP pour l'ensemble
* de paramètres donné.
*
* @param key: le secret partagé, encodé en HEX
* @param time: une valeur qui reflète un temps
* @param returnDigits: nombre de chiffres à retourner
*
* @return: une chaîne numérique en base 10
*/
public static String generateTOTP512(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA512");
}
/**
* Cette méthode génère une valeur TOTP pour l'ensemble
* de paramètres donné.
*
* @param key: le secret partagé, encodé en HEX
* @param time: une valeur qui reflète un temps
* @param returnDigits: nombre de chiffres à retourner
* @param crypto: la fonction cryptographique à utiliser
*
* @return: une chaîne numérique en 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);
}
}
}