OTP原理及實現

公司項目需求:爲局域網之外的網站後臺用戶開發動態密保的功能。
在當前的現有設備下,最方便實現的就兩種:一、經過短信獲取動態密碼登陸;二、經過手機令牌來實現。 java

OTP 原理:
相關的博文:http://blog.csdn.net/goldboar/article/details/7065948
百度百科: http://baike.baidu.com/view/1505735.htm
otp從技術來分有三種形式, 時間同步、事件同步、挑戰/應答。
(1) 時間同步
原理是基於 動態令牌動態口令驗證服務器的時間比對,基於 時間同步令牌,通常每60秒產生一個新口令,要求服務器可以十分精確的保持正確的時鐘,同時對其令牌的晶振頻率有嚴格的要求,這種技術對應的終端是硬件令牌。
(2)事件同步
基於事件同步的令牌,其原理是經過某一特定的事件次序及相同的種子值做爲輸入,經過HASH算法中運算出一致的密碼。
(3)挑戰/應答
經常使用於的網上業務,在網站/應答上輸入 服務端下發的 挑戰碼動態令牌輸入該挑戰碼,經過內置的算法上生成一個6/8位的隨機數字,口令一次有效,這種技術目前應用最爲廣泛,包括刮刮卡、短信密碼、動態令牌也有挑戰/應答形式。

主流的動態令牌技術是時間同步和挑戰/應答兩種形式。 jquery

項目採用:
https://code.google.com/p/androidtoken/ 實現TOTP動態口令登陸
android token 該項目支持HOTP (事件令牌)和TOTP (時間令牌)規範
配置令牌支持:KeyUriFormat和QR碼,以及手動建立;

項目實現:
    我這裏採用添加方便的qr碼,也就是常見的二維碼來實現用戶經過手機來綁定一個token;
首先,須要有的就是服務器端和客戶端都共有的一個seed。 android

private final static String NUM_CHAR = "0123456789";  
   private static int charLen = NUM_CHAR.length();  


   /**  
    * 根據系統時間得到指定位數的隨機數  
    * @param randomNumberDigit 隨機數的位數  
    *  @return  得到的隨機數  
    */  
   public static String getRandomNumber(int randomNumberDigit) {  
      long seed = System.currentTimeMillis();// 得到系統時間,做爲生成隨機數的種子  
      StringBuffer sb = new StringBuffer();// 裝載生成的隨機數  
      Random random = new Random(seed);// 調用種子生成隨機數  
      for (int i = 0; i < randomNumberDigit; i++) {  
         sb.append(NUM_CHAR.charAt(random.nextInt(charLen)));  
      }  


      return sb.toString();  
   }

經過http://www.oschina.net/p/jquery-qrcode-js能夠很方便的生成二維碼。
二維碼的內容
otpauth://totp/oa?secret=63985989418859891633&period=60&digits=8(android 支持這個協議,能直接掃描讀取,添加一個token)
secret:密鑰,也就是上面生成的seed;period:每60秒生成一次;digits:生成的隨機碼長度。

經過動態口令驗證之後,服務器端也保存上seed;須要登錄的時候,服務器端生成動態口令和手機的來次對比就好了。 git

服務器端生成動態密碼的方法: 算法

/**
	 * 每60秒生成1個8位動態密碼
	 * 
	 * @param seed
	 * @return
	 */
	public static String getTOTP(String seed) {
		long T0 = 0;
		long X = 60;
		Calendar cal = Calendar.getInstance();
		long time = cal.getTimeInMillis() / 1000;
		String steps = "0";
		try {
			long T = (time - T0) / X;
			steps = Long.toHexString(T).toUpperCase();
			while (steps.length() < 16)
				steps = "0" + steps;
			return generateTOTP(seed, steps, "8", "HmacSHA1");
		} catch (final Exception e) {
			System.out.println("Error : " + e);
			return "生成動態口令失敗";
		}
	}
/**
	 * 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 String generateTOTP(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;
	}
ps:直接下載的那個android token有廣告,升級一下就沒了
相關文章
相關標籤/搜索