學着車輪的otp

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章