学着车轮的otp

有java、js、php、python、c版本,可以加入自己程序里,用来做登录验证。

代码放在https://gitee.com/superzlc/otp

另外微信小程序“动态口令”能够提供otp验证

java:

package com.superzlc.utils;

import java.util.HashMap;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

public abstract class OTP {

	private static final String MAC_NAME = "HmacSHA1";

	public static void main(String[] args) {

		HOTP p1 = new HOTP("123456", 0);
		System.out.println(p1.gen()); // 186818
		System.out.println();

		System.out.println(p1.gen(1234)); // 263197
		System.out.println(p1.verify("263197", 1234));
		System.out.println();

		TOTP p2 = new TOTP("123456", 3, 30);
		System.out.println(p2.gen(1614054214456L)); // 330925
		System.out.println(p2.verify("330925", 1614054214456L));

		TOTP p3 = new TOTP("helloworld", 3, 30);
		p3.setSecret("helloworld", "string");
		p3.setSecret("68656c6c6f776f726c64", "hex");
		p3.setSecret("aGVsbG93b3JsZA==", "base64");
		System.out.println(p3.gen(1614054214456L)); // 759440
		System.out.println(p3.verify("759440", 1614054214456L));

	}

	// ----------------
	public static class HOTP {
		private byte[] secret = null;
		private long counter = 0;

		public HOTP() {
			this.secret = null;
			this.counter = 0;
		}

		public HOTP(String secret, long counter) {
			this.secret = secret == null ? null : secret.getBytes();
			this.counter = counter;
		}

		public void setSecret(byte[] secret) {
			this.secret = secret;
		}

		public void setSecret(String secret, String format) {
			this.secret = _convertSecret(secret, format);
		}

		//
		public String gen() {
			return gen(this.counter);
		}

		public boolean verify(String token) {
			if (verify(token, this.counter)) {
				this.counter++;
				return true;
			}
			return false;
		}

		public String gen(long counter) {
			return _gen(secret, counter);
		}

		public boolean verify(String token, long counter) {
			return token.equals(gen(counter));
		}

		// ------------
		private static String _gen(byte[] secret, long counter) {
			byte[] data = new byte[8];
			for (int i = 7; i >= 0; i--) {
				data[i] = (byte) (counter & 0xFF);
				counter >>>= 8;
			}
			byte[] hash = _hmacSHA1Encrypt(secret, data);
			int offset = hash[19] & 0xf;
			int num = (hash[offset] & 0x7f) << 24 |
				(hash[offset + 1] & 0xff) << 16 |
				(hash[offset + 2] & 0xff) << 8  |
				(hash[offset + 3] & 0xff);
			num = num % 1000000;
			return String.format("%06d", num);
		}

		//
		private static byte[] _hmacSHA1Encrypt(byte[] secret, byte[] data) {
			try {
				Mac mac = Mac.getInstance(MAC_NAME);
				SecretKey secretKey = new SecretKeySpec(secret, MAC_NAME);
				mac.init(secretKey);
				return mac.doFinal(data);
			} catch(Exception e) {
				throw new RuntimeException("hmacSHA1加密失败", e);
			}
		}

		//
		private static byte[] _convertSecret(String secret, String format) {
			if ("hex".equals(format)) {
				try {
					return Hex.decodeHex(secret);
				} catch(Exception e) {
					throw new RuntimeException("参数错误");
				}
			} else if ("base64".equals(format)) {
				return Base64.decodeBase64(secret);
			} else if ("string".equals(format)) {
				return secret.getBytes();
			} else {
				throw new RuntimeException("参数错误");
			}
		}

	}

	public static class TOTP {
		private HOTP hotp = null;
		private int window = 3;
		private int interval = 30;

		public TOTP() {
			this.hotp = new HOTP();
			this.window = 3;
			this.interval = 30;
		}

		public TOTP(String secret, int window, int interval) {
			this.hotp = new HOTP(secret, 0);
			this.window = window;
			this.interval = interval;
		}

		public void setSecret(byte[] secret) {
			this.hotp.setSecret(secret);
		}

		public void setSecret(String secret, String format) {
			this.hotp.setSecret(secret, format);
		}

		//
		public String gen() {
			long timestamp = System.currentTimeMillis();
			return gen(timestamp);
		}

		public String gen(long timestamp) {
			long counter = (long) (timestamp / 1000 / this.interval);
			return this.hotp.gen(counter);
		}

		public HashMap<String, Object> gen2() {
			long timestamp = System.currentTimeMillis();
			return gen2(timestamp);
		}

		public HashMap<String, Object> gen2(long timestamp) {
			long timestamp_second = timestamp / 1000;
			long counter = (long) (timestamp_second / this.interval);
			String token = this.hotp.gen(counter);
			long step = timestamp_second % this.interval;
			long refresh_time = counter * this.interval;

			HashMap<String, Object> map = new HashMap<>();
			map.put("token", token);
			map.put("loop_length", this.interval);
			map.put("loop_step", step);
			map.put("refresh_interval", this.interval * 1000);
			map.put("refresh_time", refresh_time * 1000);
			map.put("timestamp", timestamp);
			return map;
		}

		public boolean verify(String token) {
			return this.verify(token, System.currentTimeMillis());
		}

		public boolean verify(String token, long timestamp) {
			long counter = (long) (timestamp / 1000 / interval);

			for (int i = 0, w = 0; ; i++) {
				if (token.equals(this.hotp.gen(counter - i)))
					return true;
				if (++w >= window)
					break;
				if (i > 0) {
					if (token.equals(this.hotp.gen(counter + i)))
						return true;
					if (++w > window)
						break;
				}
			}
			return false;
		}

	}

}

php:

<?php

class HOTP {
	private $secret = null;
	private $counter = 0;
	
	public function __construct($secret = null, $counter = 0) {
		$this->secret = $secret;
		$this->counter = $counter;
	}
	
	public function setSecret($secret, $format = null) {
		$this->secret = self::_convertSecret($secret, $format);
	}
	
	public function gen($counter = null) {
		if ($counter === null) {
			if ($this->counter === null) {
				$this->counter = 0;
			}
			$counter = $this->counter;
		}
		return self::_gen($this->secret, $counter);
	}
	
	public function verify($token, $counter = null) {
		if ($counter === null) {
			if ($this->counter === null) {
				$this->counter = 0;
			}
			$counter = $this->counter;
			if ($token === $this->gen(counter)) {
				$this->counter += 1;
				return true;
			}
			return false;
		}
		return $token === $this->gen($counter);
	}
	
	private static function _gen($secret, $counter) {
		$data = pack('NN', $counter / 4294967296, $counter & 0xFFFFFFFF);
		$hash = hash_hmac('sha1', $data, $secret, true);
		$offset = ord($hash[19]) &0xf;
		$num = (ord($hash[$offset]) & 0x7F) << 24 |
			(ord($hash[$offset + 1]) & 0xFF) << 16 |
			(ord($hash[$offset + 2]) & 0xFF) << 8  |
			(ord($hash[$offset + 3]) & 0xFF);
		$num = $num % 1000000;
		return sprintf("%06d", $num);
	}
	
	private static function _convertSecret($secret, $format) {
		if (!is_string($secret))
			throw Exception('参数错误');
		if ('hex' === $format)
			return hex2bin($secret);
		else if ('base64' === $format)
			return base64_decode($secret);
		else if ('string' === $format || null === $format)
			return $secret;
		else
			throw Exception('参数错误');
	}
}

class TOTP {
	private $hotp;
	private $window = 3;
	private $interval = 30;
	
	public function __construct($secret = null, $window = 3, $interval = 30) {
		$this->hotp = new HOTP($secret, 0);
		$this->window = $window;
		$this->interval = $interval;
	}
	
	public function setSecret($secret, $format = null) {
		$this->hotp->setSecret($secret, $format);
	}
	
	public function gen($timestamp = null) {
		$timestamp = empty($timestamp) ? (time() * 1000) : $timestamp;
		$counter = floor($timestamp / 1000 / $this->interval);
		return $this->hotp->gen($counter);
	}
	
	public function gen2($timestamp = null) {
		$timestamp = empty($timestamp) ? (time() * 1000) : $timestamp;
		$timestamp_second = $timestamp / 1000;
		$counter = floor($timestamp_second / $this->interval);
		$token = $this->hotp->gen($counter);
		$step = $timestamp_second % $this->interval;
		$refresh_time = $counter * $this->interval;
		return array("token" => $token,
			"loop_length" => $this->interval,
			"loop_step" => $step,
			"refresh_interval" => $this->interval * 1000,
			"refresh_time" => $refresh_time * 1000,
			"timestamp" => $timestamp
			);
	}
	
	public function verify($token, $timestamp = null) {
		$$timestamp = empty($timestamp) ? (time() * 1000) : $timestamp;
		$counter = floor($timestamp / 1000 / $this->interval);
		
		for ($i = 0, $w = 0; ; $i++) {
			if ($token === $this->hotp->gen($counter - $i))
				return true;
			if (++$w >= $this->window)
				break;
			if ($i > 0) {
				if ($token === $this->hotp->gen($counter + $i))
					return true;
				if (++$w >= $this->window)
					break;
			}
		}
		return false;
	}
}

/**

function test() {
	echo "\n<pre>\n";
	
	$p1 = new HOTP("123456", 0);
	echo $p1->gen() . "\n"; # 186818
	echo $p1->gen(1234) . "\n"; # 263197
	echo ($p1->verify('263197', 1234) ? "TRUE" : "FALSE") . "\n";

	$p2 = new TOTP("123456", 3, 30);
	echo $p2->gen(1614054214456) . "\n"; # 330925
	echo ($p2->verify('330925', 1614054214456) ? "TRUE" : "FALSE") . "\n";

	$p3 = new TOTP('helloworld', 3, 30);
	$p3->setSecret('helloworld', 'string');
	$p3->setSecret('68656c6c6f776f726c64', 'hex');
	$p3->setSecret('aGVsbG93b3JsZA==', 'base64');
	echo $p3->gen(1614054214456) . "\n"; # 759440
	echo ($p3->verify('759440', 1614054214456) ? "TRUE" : "FALSE") . "\n";

	echo "\n</pre>\n";
}
test();
**/

js:

// 需要CryptoJS
const CryptoJS = require('./crypto-js.js')

// ==================================================================================
var HOTP = function(secret = null, counter = 0) {
	this.secret = secret == null ? null : CryptoJS.enc.Utf8.parse(secret);
	this.counter = counter;
};

HOTP.prototype.setSecret = function(secret, format = null) {
	this.secret = this._convertSecret(secret, format);
}

HOTP.prototype.gen = function(counter = null) {
	if (counter === null) {
		if (this.counter === undefined || this.counter === null) {
			this.counter = 0;
		}
		counter = this.counter;
	}
	return this._gen(this.secret, counter);
}

HOTP.prototype.verify = function(token, counter = null) {
	if (counter === null) {
		if (this.counter === undefined || this.counter === null) {
			this.counter = 0;
		}
		counter = this.counter;
		if (token === this.gen(counter)) {
			this.counter += 1;
			return true;
		}
		return false;
	}
	return token === this.gen(counter);
}

HOTP.prototype._gen = function(secret, counter) {
	
	var data = CryptoJS.lib.WordArray.create([counter / 4294967296, counter & 0xffffffff]);
	var hash = CryptoJS.HmacSHA1(data, secret);
	
	var hash2 = [];
	for (var i = 0, j = 0; i < 5; i++) {
		var v = hash.words[i];
		hash2[j++] = ((v >> 24) & 0xFF);
		hash2[j++] = ((v >> 16) & 0xFF);
		hash2[j++] = ((v >>  8) & 0xFF);
		hash2[j++] = ((v >>  0) & 0xFF);
	}
	
	var offset = hash2[19] & 0xf;
	var num = (hash2[offset] & 0x7F) << 24 |
		(hash2[offset + 1] & 0xFF) << 16 |
		(hash2[offset + 2] & 0xFF) << 8  |
		(hash2[offset + 3] & 0xFF);

	
	num = num % 1000000;
	if (num >= 100000) {
		return String(num);
	}
	num = "000000" + num;
	return num.substring(num.length - 6);
}

HOTP.prototype._convertSecret = function(secret, format) {
	if (typeof(secret) === "string") {
		if ('hex' === format)
			return CryptoJS.enc.Hex.parse(secret);
		else if ('base64' === format)
			return CryptoJS.enc.Base64.parse(secret);
		else if ('string' === format)
			return CryptoJS.enc.Utf8.parse(secret);
		else
			throw Error('参数错误');
	} else if (secret instanceof CryptoJS.constructor) { // XXX WordArray
		return secret;
	} else {
		throw Error('参数错误');
	}
}

// ==================================================================================
var TOTP = function(secret = null, window = 3, interval = 30) {
	this.hotp = new HOTP(secret, 0);
	this.window = window;
	this.interval = interval;
}

TOTP.prototype.setSecret = function(secret, format = null) {
	this.hotp.setSecret(secret, format);
}

TOTP.prototype.gen = function(timestamp = null) {
	timestamp = timestamp === null ? new Date().getTime() : timestamp;
	var counter = Math.floor(timestamp / 1000 / this.interval);
	return this.hotp.gen(counter);
}

TOTP.prototype.gen2 = function(timestamp = null) {
	timestamp = timestamp === null ? new Date().getTime() : timestamp;
	var timestamp_second = Math.floor(timestamp / 1000);
	var counter = Math.floor(timestamp_second / this.interval);
	var token = this.hotp.gen(counter);
	var step = timestamp_second % this.interval;
	var refresh_time = tcounter * this.interval;
	return {
		token: token, // 生成的令牌
		loop_length: this.interval, // 循环角度,循环长度
		loop_step: step, // 循环角度,循环到的步骤
		refresh_interval: this.interval * 1000, // 刷新周期
		refresh_time: refresh_time * 1000, // 刷新时间
		timestamp: timestamp, // 当前时间
	};
}

TOTP.prototype.verify = function(token, timestamp = null) {
	timestamp = timestamp === null ? new Date().getTime() : timestamp;
	var counter = Math.floor(timestamp / 1000 / this.interval);
	for (var i = 0, w = 0; ; i++) {
		if (token === this.hotp.gen(counter - i))
			return true;
		if (++w >= this.window)
			break;
		if (i > 0) {
			if (token === this.hotp.gen(counter + i))
				return true;
			if (++w >= this.window)
				break;
		}
	}
	return false;
}

module.exports = {
  HOTP: HOTP,
  TOTP: TOTP,
}

/*
!function() {
	var p1 = new HOTP("123456", 0);
	console.log(p1.gen()); // 186818
	console.log(p1.gen(1234)); // 263197
	console.log(p1.verify("263197", 1234));

	var p2 = new TOTP("123456", 3, 30);
	console.log(p2.gen(1614054214456)); // 330925
	console.log(p2.verify("330925", 1614054214456));

	var p3 = new TOTP("helloworld", 3, 30);
	p3.setSecret("helloworld", "string");
	p3.setSecret("68656c6c6f776f726c64", "hex");
	p3.setSecret("aGVsbG93b3JsZA==", "base64");
	console.log(p3.gen(1614054214456)); // 759440
	console.log(p3.verify("759440", 1614054214456));
}();
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值