java和php版本
java:
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class OTP {
private static final String MAC_NAME = "HmacSHA1";
public static void main(String[] args) {
HOTP p = new HOTP();
System.out.println(p.gen());
System.out.println(p.gen());
System.out.println(p.gen());
System.out.println(p.gen());
System.out.println(p.gen(0));
System.out.println(p.gen(1));
System.out.println(p.gen(2));
System.out.println(p.verify("721100", 2));
TOTP p2 = new TOTP();
System.out.println(p2.gen());
System.out.println(p2.verify("212876"));
}
public static class HOTP {
private String key = "123456";
private int window = 3;
private int counter = 0;
public HOTP() {
}
public HOTP(String key, int window, int counter) {
this.key = key == null ? "" : key;
this.window = window < 0 || window > 100 ? 50 : window;
this.counter = counter;
}
public String gen() {
String ret = gen(this.counter);
this.counter++;
return ret;
}
public Integer verify(String token) {
return verify(token, this.counter);
}
public String gen(int counter) {
byte[] bytes = hmacSHA1Encrypt(intToBytes(counter));
// 轉數字
int v = convert(bytes);
// 留6位
v = v % 1000000;
return String.format("%06d", v);
}
public Integer verify(String token, int counter) {
for (int i = 0; i < window; i++) {
{
int tcounter = counter - i;
if (token.equals(gen(tcounter)))
return i;
}
if (i == 0)
continue;
{
int tcounter = counter + i;
if (token.equals(gen(tcounter)))
return i;
}
}
return null;
}
// 怎麼算
private int convert(byte[] bytes20) {
// 最後一個字節得到起始位置0-15
int offset = bytes20[19] & 0xf;
//按起始位置截取4字節累加
int v = (bytes20[offset] & 0x7f) << 24 |
(bytes20[offset + 1] & 0xff) << 16 |
(bytes20[offset + 2] & 0xff) << 8 |
(bytes20[offset + 3] & 0xff);
return v;
}
//
private Mac mac;
private byte[] hmacSHA1Encrypt(byte[] encryptDataBytes) {
if (mac == null) {
try {
mac = Mac.getInstance(MAC_NAME);
SecretKey secretKey = new SecretKeySpec(key.getBytes(), MAC_NAME);
mac.init(secretKey);
} catch(Exception e) {
throw new RuntimeException("hmacSHA1加密失敗", e);
}
}
return mac.doFinal(encryptDataBytes);
}
private static byte[] intToBytes(int v) {
byte[] bytes = new byte[4];
bytes[0] = (byte) ((v >>> 24) & 0xFF);
bytes[1] = (byte) ((v >>> 16) & 0xFF);
bytes[2] = (byte) ((v >>> 8) & 0xFF);
bytes[3] = (byte) ((v >>> 0) & 0xFF);
return bytes;
}
}
public static class TOTP {
private HOTP hotp;
private int second = 30;
public TOTP() {
this.hotp = new HOTP("123456", 2, 0);
}
public TOTP(String key, int window, int second) {
this.hotp = new HOTP(key, window, 0);
this.second = second;
}
public String gen() {
long now = System.currentTimeMillis();
int tcounter = (int) (now / 1000 / second);
return this.hotp.gen(tcounter);
}
public Integer verify(String token) {
long now = System.currentTimeMillis();
int tcounter = (int) (now / 1000 / second);
return this.hotp.verify(token, tcounter);
}
}
}
php:
<?php
class HOTP {
private $key = '123456';
private $window = 3;
private $counter = 0;
public function __construct($key = "123456", $window = 3, $counter = 0) {
$this->key = empty($key) ? "123456" : $key;
$this->window = $window < 0 || $window > 100 ? 50 : $window;
$this->counter = $counter;
}
public function gen($counter = null) {
if ($counter === null) {
if ($this->counter === null) {
$this->counter = 0;
}
$counter = $this->counter;
$ret = $this->gen($counter);
$this->counter += 1;
return $ret;
}
$dataStr = $this->intToString($counter);
$bytes = $this->hmacSHA1Encrypt($dataStr);
$num = $this->convert($bytes);
$num = $num % 1000000;
return sprintf("%06d", $num);
}
public function verify($token, $counter = null) {
if ($counter === null) {
if ($this->counter === null) {
$this->counter = 0;
}
$counter = $this->counter;
return $this->verify($token, $counter);
}
for ($i = 0; $i < $this->window; $i++) {
{
$tcounter = $counter - $i;
if ($token === $this->gen($tcounter))
return $i;
}
if ($i === 0)
continue;
{
$tcounter = $counter + $i;
if ($token === $this->gen($tcounter))
return $i;
}
}
}
private function hmacSHA1Encrypt($dataStr) {
$keyStr = $this->key;
$hash = hash_hmac('sha1', $dataStr, $keyStr);
$bytes = Array();
foreach(str_split($hash, 2) as $hex) {
$hmac[] = hexdec($hex);
}
return $hmac;
}
private function convert($bytes20) {
$offset = $bytes20[19] & 0xf;
$v = ($bytes20[$offset] & 0x7F) << 24 |
($bytes20[$offset + 1] & 0xFF) << 16 |
($bytes20[$offset + 2] & 0xFF) << 8 |
($bytes20[$offset + 3] & 0xFF);
return $v;
}
private function bytesToString($bytes) {
$str = '';
foreach($bytes as $ch) {
$str .= chr($ch);
}
return $str;
}
private function intToBytes($v) {
$bytes = Array();
$bytes[] = (($v >> 24) & 0xFF);
$bytes[] = (($v >> 16) & 0xFF);
$bytes[] = (($v >> 8) & 0xFF);
$bytes[] = (($v >> 0) & 0xFF);
return $bytes;
}
private function intToString($v) {
$bytes = $this->intToBytes($v);
$ret = $this->bytesToString($bytes);
return $ret;
}
}
class TOTP {
private $hotp;
private $second = 30;
public function __construct($key = "123456", $window = 3, $second = 30) {
$this->hotp = new HOTP($key, $window, 0);
$this->second = $second;
}
public function gen() {
$now = time();
$tcounter = (int)($now / $this->second);
return $this->hotp->gen($tcounter);
}
public function verify($token) {
$now = time();
$tcounter = (int)($now / $this->second);
return $this->hotp->verify($token, $tcounter);
}
}
/*
$p = new HOTP();
echo($p->gen() . "<br>");
echo($p->gen() . "<br>");
echo($p->gen() . "<br>");
echo($p->gen() . "<br>");
echo($p->gen(0) . "<br>");
echo($p->gen(1) . "<br>");
echo($p->gen(2) . "<br>");
echo($p->verify("721100", 2) . "<br>");
$p2 = new TOTP();
echo $p2->gen() . "<br>";
*/
/**
$ttoken = "123123";
$token = "509557";
$totp = new TOTP($ttoken, 3, 30);
echo $totp->gen() . "<br>";
echo $totp->verify($token) . "<br>";
**/