メインコンテンツまでスキップ

Appendix A. TOTP Algorithm: Reference Implementation (付録 A. TOTP アルゴリズム: リファレンス実装)

Appendix A. TOTP Algorithm: Reference Implementation (TOTP アルゴリズム: リファレンス実装)

/**
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;


/**
* これは OATH TOTP アルゴリズムの実装例です。
* 詳細については www.openauthentication.org をご覧ください。
*
* @author Johan Rydell, PortWise, Inc.
*/

public class TOTP {

private TOTP() {}

/**
* このメソッドは JCE を使用して暗号アルゴリズムを提供します。
* HMAC は暗号ハッシュアルゴリズムをパラメータとして、
* ハッシュベースのメッセージ認証コードを計算します。
*
* @param crypto: 暗号アルゴリズム (HmacSHA1, HmacSHA256,
* HmacSHA512)
* @param keyBytes: HMAC 鍵に使用するバイト
* @param 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);
}
}


/**
* このメソッドは HEX 文字列を Byte[] に変換します
*
* @param hex: HEX 文字列
*
* @return: バイト配列
*/

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 };

/**
* このメソッドは与えられたパラメータセットに対して
* TOTP 値を生成します。
*
* @param key: 共有秘密, HEX エンコード
* @param time: 時間を反映する値
* @param returnDigits: 返す桁数
*
* @return: 10 進数の数値文字列
*/

public static String generateTOTP(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA1");
}


/**
* このメソッドは与えられたパラメータセットに対して
* TOTP 値を生成します。
*
* @param key: 共有秘密, HEX エンコード
* @param time: 時間を反映する値
* @param returnDigits: 返す桁数
*
* @return: 10 進数の数値文字列
*/

public static String generateTOTP256(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA256");
}

/**
* このメソッドは与えられたパラメータセットに対して
* TOTP 値を生成します。
*
* @param key: 共有秘密, HEX エンコード
* @param time: 時間を反映する値
* @param returnDigits: 返す桁数
*
* @return: 10 進数の数値文字列
*/

public static String generateTOTP512(String key,
String time,
String returnDigits){
return generateTOTP(key, time, returnDigits, "HmacSHA512");
}


/**
* このメソッドは与えられたパラメータセットに対して
* TOTP 値を生成します。
*
* @param key: 共有秘密, HEX エンコード
* @param time: 時間を反映する値
* @param returnDigits: 返す桁数
* @param crypto: 使用する暗号関数
*
* @return: 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);
}
}
}