|
@@ -7,214 +7,199 @@ import java.text.DateFormat;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
import java.util.Date;
|
|
|
import java.util.TimeZone;
|
|
|
-
|
|
|
import javax.crypto.Mac;
|
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
|
|
|
public class TimeBasedOTP {
|
|
|
-
|
|
|
+ // 0 1 2 3 4 5 6 7 8
|
|
|
private static final int[] DIGITS_POWER
|
|
|
- // 0 1 2 3 4 5 6 7 8
|
|
|
- = {1,10,100,1000,10000,100000,1000000,10000000,100000000 };
|
|
|
-
|
|
|
- static String seed64 = "";
|
|
|
-
|
|
|
- static String steps = "0";
|
|
|
-
|
|
|
- static String steps2 = "0";
|
|
|
-
|
|
|
- TimeBasedOTP() {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 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_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);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * This method converts a 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[i] = bArray[i+1];
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * This method generates a OTP value for the given
|
|
|
- * set of parameters.
|
|
|
- * Default Crypto HmacSHA512
|
|
|
- * @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 genOTP(String key,
|
|
|
- String time,
|
|
|
- String returnDigits){
|
|
|
-
|
|
|
- return generateOTP(key, time, returnDigits, "HmacSHA1");
|
|
|
- }
|
|
|
-
|
|
|
- /* * This method generates a OTP value for the given
|
|
|
- * set of parameters.
|
|
|
- * Crypto HmacSHA1
|
|
|
- * @param key
|
|
|
- * @param time
|
|
|
- * @param returnDigits
|
|
|
- * @return
|
|
|
- */
|
|
|
- public static String generateTOTPHmacSHA1(String key,
|
|
|
- String time,
|
|
|
- String returnDigits){
|
|
|
- return generateOTP(key, time, returnDigits, "HmacSHA1");
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /* * This method generates a OTP value for the given
|
|
|
- * set of parameters.
|
|
|
- * Crypto HmacSHA256
|
|
|
- * @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 genOTPHmacSHA256(String key,
|
|
|
- String time,
|
|
|
- String returnDigits){
|
|
|
- return generateOTP(key, time, returnDigits, "HmacSHA256");
|
|
|
- }
|
|
|
-
|
|
|
- /* *
|
|
|
- * This method generates a OTP value for the given
|
|
|
- * set of parameters.
|
|
|
- * Crypto HmacSHA256
|
|
|
- * @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 genOTPHmacSHA512(String key,
|
|
|
- String time,
|
|
|
- String returnDigits){
|
|
|
- return generateOTP(key, time, returnDigits, "HmacSHA512");
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * This method generates a 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
|
|
|
- */
|
|
|
- public static void actualiseTime(){
|
|
|
+ = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
|
|
+
|
|
|
+ static String seed64 = "";
|
|
|
+
|
|
|
+ static String steps = "0";
|
|
|
+
|
|
|
+ static String steps2 = "0";
|
|
|
+
|
|
|
+ TimeBasedOTP() {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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_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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method converts a 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[i] = bArray[i + 1];
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method generates a OTP value for the given set of parameters. Default
|
|
|
+ * Crypto HmacSHA512
|
|
|
+ *
|
|
|
+ * @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 genOTP(String key, String time, String returnDigits) {
|
|
|
+
|
|
|
+ return generateOTP(key, time, returnDigits, "HmacSHA1");
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * * This method generates a OTP value for the given set of parameters. Crypto
|
|
|
+ * HmacSHA1
|
|
|
+ *
|
|
|
+ * @param key
|
|
|
+ *
|
|
|
+ * @param time
|
|
|
+ *
|
|
|
+ * @param returnDigits
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static String generateTOTPHmacSHA1(String key, String time, String returnDigits) {
|
|
|
+ return generateOTP(key, time, returnDigits, "HmacSHA1");
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * * This method generates a OTP value for the given set of parameters. Crypto
|
|
|
+ * HmacSHA256
|
|
|
+ *
|
|
|
+ * @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 genOTPHmacSHA256(String key, String time, String returnDigits) {
|
|
|
+ return generateOTP(key, time, returnDigits, "HmacSHA256");
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * * This method generates a OTP value for the given set of parameters. Crypto
|
|
|
+ * HmacSHA256
|
|
|
+ *
|
|
|
+ * @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 genOTPHmacSHA512(String key, String time, String returnDigits) {
|
|
|
+ return generateOTP(key, time, returnDigits, "HmacSHA512");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method generates a TOTP value for the given set of parameters.
|
|
|
+ */
|
|
|
+ public static void actualiseTime() {
|
|
|
// double random = Math.random();
|
|
|
- long T0 = 0;
|
|
|
- long X = 30;
|
|
|
- Date d = new Date();
|
|
|
- Long k = d.getTime()/1000;
|
|
|
-
|
|
|
- long testTime[] = {d.getTime()};
|
|
|
- DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
- df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
|
-
|
|
|
- for (int i=0; i<testTime.length; i++) {
|
|
|
- long T = (k - T0)/X;
|
|
|
- long T2 = ((k - T0)/X)-1;
|
|
|
- // System.out.println(T);
|
|
|
- steps = Long.toHexString(T).toUpperCase();
|
|
|
- steps2 = Long.toHexString(T2).toUpperCase();
|
|
|
- while (steps.length() < 16) steps = "0" + steps;
|
|
|
- //String fmtTime = String.format("%1$-11s", k/1000);
|
|
|
- //String utcTime = df.format(new Date(testTime[i]));
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public static String generateOTP(String key,
|
|
|
- String time,
|
|
|
- String returnDigits,
|
|
|
- String crypto){
|
|
|
- int codeDigits = Integer.decode(returnDigits).intValue();
|
|
|
-
|
|
|
- String result = null;
|
|
|
-
|
|
|
- // Using the counter
|
|
|
- // First 8 bytes are for the movingFactor
|
|
|
- // Compliant with base RFC 4226 (HOTP)
|
|
|
- while (time.length() < 16 )
|
|
|
- time = "0" + time;
|
|
|
-
|
|
|
- // Get the HEX in a Byte[]
|
|
|
- byte[] msg = hexStr2Bytes(time);
|
|
|
- byte[] k = hexStr2Bytes(key);
|
|
|
-
|
|
|
-
|
|
|
- byte[] hash = hmac_sha(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;
|
|
|
- }
|
|
|
-
|
|
|
+ long T0 = 0;
|
|
|
+ long X = 30;
|
|
|
+ Date d = new Date();
|
|
|
+ Long k = d.getTime() / 1000;
|
|
|
+
|
|
|
+ long testTime[] = { d.getTime() };
|
|
|
+ DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
|
+
|
|
|
+ for (int i = 0; i < testTime.length; i++) {
|
|
|
+ long T = (k - T0) / X;
|
|
|
+ long T2 = ((k - T0) / X) - 1;
|
|
|
+ // System.out.println(T);
|
|
|
+ steps = Long.toHexString(T).toUpperCase();
|
|
|
+ steps2 = Long.toHexString(T2).toUpperCase();
|
|
|
+ while (steps.length() < 16) {
|
|
|
+ steps = "0" + steps;
|
|
|
+ }
|
|
|
+ //String fmtTime = String.format("%1$-11s", k/1000);
|
|
|
+ //String utcTime = df.format(new Date(testTime[i]));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * generateOTP.
|
|
|
+ * @param key String
|
|
|
+ * @param time String
|
|
|
+ * @param returnDigits String
|
|
|
+ * @param crypto String
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static String generateOTP(String key, String time, String returnDigits, String crypto) {
|
|
|
+ int codeDigits = Integer.decode(returnDigits).intValue();
|
|
|
+
|
|
|
+ String result = null;
|
|
|
+
|
|
|
+ // Using the counter
|
|
|
+ // First 8 bytes are for the movingFactor
|
|
|
+ // Compliant with base RFC 4226 (HOTP)
|
|
|
+ while (time.length() < 16) {
|
|
|
+ time = "0" + time;
|
|
|
+ }
|
|
|
+ // Get the HEX in a Byte[]
|
|
|
+ byte[] msg = hexStr2Bytes(time);
|
|
|
+ byte[] k = hexStr2Bytes(key);
|
|
|
+
|
|
|
+ byte[] hash = hmac_sha(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;
|
|
|
+ }
|
|
|
|
|
|
}
|