Hallo zusammen,
ich probiere gerade den Google Authenticator so nachzubauen, dass man kein Handy mehr bräuchte, sondern einfach seinen 30-stelligen Code, welchen man von einer Plattform bekommt in den Programmcode eingibt und das Programm daraufhin ein 6-Stelligen Code zurückgibt.
Zur Hilfe habe ich den Code von https://tools.ietf.org/id/draft-mraihi-totp-timebased-06.html kopiert und etwas überarbeitet. Leider funktioniert es nicht - der Code der generiert wird, ist falsch.
Änderungen die ich vorgenommen habe:
- Statt 8 Stellen nur 6 Stellen TOTP
- Die Uhrzeiten mit unixTime "automatisiert"
- Einige Notizen hinzugefügt
Den Seed in Zeile 196 habe ich sowohl als Ausgangsseed als auch in HEX Form als STring eingegeben → Ohne Erfolg.
Weiß jemand von euch, wo der Fehler liegt? Google verwendet meines Wissens nach SHA1 als Verschlüsselungsform
[CODE lang="java" title="TOTP-Klasse"]package hash;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.util.TimeZone;
/**
* This an example implementation of the OATH TOTP algorithm.
* Visit www.openauthentication.org for more information.
*
* @author Johan Rydell, PortWise, Inc.
*
*/
//https://tools.ietf.org/id/draft-mraihi-totp-timebased-06.html
public class TOTP {
private TOTP() {}
/**
* This method uses the JCE to provide the crypto
* algorithm.
* HMAC computes a Hashed Message Authentication Code with the
* crypto hash algorithm as a parameter.
*
* @param crypto the crypto algorithm (HmacSHA1, HmacSHA256,
* HmacSHA512)
* @param keyBytes the bytes to use for the HMAC key
* @param text the message or text to be authenticated.
*/
private static byte[] hmac_sha1(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);
}
}
/**
* This method converts HEX string to Byte[]
*
* @param hex the HEX string
*
* @return A byte array
*/
private static byte[] hexStr2Bytes(String hex){
// Adding one byte to get the right conversion
// values starting with "0" can be converted
byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
// Copy all the REAL bytes, not the "first"
byte[] ret = new byte[bArray.length - 1];
for (int i = 0; i < ret.length ; i++)
ret = bArray[i+1];
return ret;
}
private static final int[] DIGITS_POWER
// 0 1 2 3 4 5 6 7 8
= {1,10,100,1000,10000,100000,1000000,10000000,100000000 };
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP(String key,
String time,
String returnDigits)
{
return generateTOTP(key, time, returnDigits, "HmacSHA1");
}
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP256(String key,
String time,
String returnDigits)
{
return generateTOTP(key, time, returnDigits, "HmacSHA256");
}
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP512(String key,
String time,
String returnDigits)
{
return generateTOTP(key, time, returnDigits, "HmacSHA512");
}
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
* @param crypto the crypto function to use
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
private static String generateTOTP(String key,
String time,
String returnDigits,
String crypto)
{
int codeDigits = Integer.decode(returnDigits).intValue();
String result = null;
byte[] hash;
// Using the counter
// First 8 bytes are for the movingFactor
// Complaint with base RFC 4226 (HOTP)
while(time.length() < 16 )
time = "0" + time;
// Get the HEX in a Byte[]
byte[] msg = hexStr2Bytes(time);
// Adding one byte to get the right conversion
byte[] k = hexStr2Bytes(key);
hash = hmac_sha1(crypto, k, msg);
// put selected bytes into result int
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 = "1122334455";
long T0 = 0;
long X = 30;
int unixTime = (int) (System.currentTimeMillis() / 1000L);
int unixTimeDESommer = (int) (System.currentTimeMillis() / 1000L)+7200;
int unixTimeDEWinter = (int) (System.currentTimeMillis() / 1000L)+3600;
System.out.println("Aktuelle Uhrzeit " +unixTime);
long testTime[] = {unixTime, unixTimeDESommer, unixTimeDEWinter};
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 - T0)/X;
steps = Long.toHexString(T).toUpperCase();
while(steps.length() < 16) steps = "0" + steps;
String fmtTime = String.format("%1$-10s", testTime);
String utcTime = df.format(new Date(testTime*1000));
//Erste 3 SPalten
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed, steps, "6",
"HmacSHA1") + "| SHA1 |");
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed, steps, "6",
"HmacSHA256") + "| SHA256 |");
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed, steps, "6",
"HmacSHA512") + "| SHA512 |");
System.out.println(
"+--------------+-----------------------+" +
"------------------+--------+--------+");
}
}catch (final Exception e){
System.out.println("Error : " + e);
}
}
}
[/CODE]
ich probiere gerade den Google Authenticator so nachzubauen, dass man kein Handy mehr bräuchte, sondern einfach seinen 30-stelligen Code, welchen man von einer Plattform bekommt in den Programmcode eingibt und das Programm daraufhin ein 6-Stelligen Code zurückgibt.
Zur Hilfe habe ich den Code von https://tools.ietf.org/id/draft-mraihi-totp-timebased-06.html kopiert und etwas überarbeitet. Leider funktioniert es nicht - der Code der generiert wird, ist falsch.
Änderungen die ich vorgenommen habe:
- Statt 8 Stellen nur 6 Stellen TOTP
- Die Uhrzeiten mit unixTime "automatisiert"
- Einige Notizen hinzugefügt
Den Seed in Zeile 196 habe ich sowohl als Ausgangsseed als auch in HEX Form als STring eingegeben → Ohne Erfolg.
Weiß jemand von euch, wo der Fehler liegt? Google verwendet meines Wissens nach SHA1 als Verschlüsselungsform
[CODE lang="java" title="TOTP-Klasse"]package hash;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.util.TimeZone;
/**
* This an example implementation of the OATH TOTP algorithm.
* Visit www.openauthentication.org for more information.
*
* @author Johan Rydell, PortWise, Inc.
*
*/
//https://tools.ietf.org/id/draft-mraihi-totp-timebased-06.html
public class TOTP {
private TOTP() {}
/**
* This method uses the JCE to provide the crypto
* algorithm.
* HMAC computes a Hashed Message Authentication Code with the
* crypto hash algorithm as a parameter.
*
* @param crypto the crypto algorithm (HmacSHA1, HmacSHA256,
* HmacSHA512)
* @param keyBytes the bytes to use for the HMAC key
* @param text the message or text to be authenticated.
*/
private static byte[] hmac_sha1(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);
}
}
/**
* This method converts HEX string to Byte[]
*
* @param hex the HEX string
*
* @return A byte array
*/
private static byte[] hexStr2Bytes(String hex){
// Adding one byte to get the right conversion
// values starting with "0" can be converted
byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
// Copy all the REAL bytes, not the "first"
byte[] ret = new byte[bArray.length - 1];
for (int i = 0; i < ret.length ; i++)
ret = bArray[i+1];
return ret;
}
private static final int[] DIGITS_POWER
// 0 1 2 3 4 5 6 7 8
= {1,10,100,1000,10000,100000,1000000,10000000,100000000 };
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP(String key,
String time,
String returnDigits)
{
return generateTOTP(key, time, returnDigits, "HmacSHA1");
}
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP256(String key,
String time,
String returnDigits)
{
return generateTOTP(key, time, returnDigits, "HmacSHA256");
}
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
public static String generateTOTP512(String key,
String time,
String returnDigits)
{
return generateTOTP(key, time, returnDigits, "HmacSHA512");
}
/**
* This method generates an TOTP value for the given
* set of parameters.
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
* @param crypto the crypto function to use
*
* @return A numeric String in base 10 that includes
* {@link truncationDigits} digits
*/
private static String generateTOTP(String key,
String time,
String returnDigits,
String crypto)
{
int codeDigits = Integer.decode(returnDigits).intValue();
String result = null;
byte[] hash;
// Using the counter
// First 8 bytes are for the movingFactor
// Complaint with base RFC 4226 (HOTP)
while(time.length() < 16 )
time = "0" + time;
// Get the HEX in a Byte[]
byte[] msg = hexStr2Bytes(time);
// Adding one byte to get the right conversion
byte[] k = hexStr2Bytes(key);
hash = hmac_sha1(crypto, k, msg);
// put selected bytes into result int
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 = "1122334455";
long T0 = 0;
long X = 30;
int unixTime = (int) (System.currentTimeMillis() / 1000L);
int unixTimeDESommer = (int) (System.currentTimeMillis() / 1000L)+7200;
int unixTimeDEWinter = (int) (System.currentTimeMillis() / 1000L)+3600;
System.out.println("Aktuelle Uhrzeit " +unixTime);
long testTime[] = {unixTime, unixTimeDESommer, unixTimeDEWinter};
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 - T0)/X;
steps = Long.toHexString(T).toUpperCase();
while(steps.length() < 16) steps = "0" + steps;
String fmtTime = String.format("%1$-10s", testTime);
String utcTime = df.format(new Date(testTime*1000));
//Erste 3 SPalten
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed, steps, "6",
"HmacSHA1") + "| SHA1 |");
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed, steps, "6",
"HmacSHA256") + "| SHA256 |");
System.out.print("| " + fmtTime + " | " + utcTime +
" | " + steps + " |");
System.out.println(generateTOTP(seed, steps, "6",
"HmacSHA512") + "| SHA512 |");
System.out.println(
"+--------------+-----------------------+" +
"------------------+--------+--------+");
}
}catch (final Exception e){
System.out.println("Error : " + e);
}
}
}
[/CODE]