手机动态口令

一.什么是OTP和TOTP

OTP全称叫One-time Password,也称动态口令,是根据专门的算法每隔60秒生成一个与时间相关的、不可预测的随机数字组合,每个口令只能使用一次,每天可以产生1440个密码。
TOTP算法(Time-based One-time Password algorithm)是一种从共享密钥和当前时间计算一次性密码的算法。

二.怎么使用动态口令登录认证

  • 1.通过qrcode在web管理台生成一个带有key的二维码,key需要加密保存数据库。
  • 2.基于js版的totp开发的跨平台的手机app,通过app扫描二维码进行绑定key,实现一个60秒切换一次口令的手机动态令牌。
  • 3.用户使用手机动态口令登录系统,后端使用基于java的totp实现结合之前存储到数据库的key进行动态口令校验。

三.js和java版totp使用hmac-sha1实现

1.totp的js实现,需要自行下载hmac-sha1的js库

  • totp.js代码
var totp = {
};

//mtimeStep 60,
totp.generateOtp = function(mSeed,mtimeStep,mOtpLength) {
                      //0 1  2   3    4     5      6       7        8          9           10
    var DIGITS_POWER = [1,10,100,1000,10000,100000,1000000,10000000,100000000, 1000000000, 10000000000];
    
    var counter = new Array(8);
    //GMT时间毫秒数
    var time = parseInt(new Date().getTime()/1000);
    //mtimeStep
    var movingFactor = parseInt(time/mtimeStep);
    
    for(var i = counter.length - 1;i >= 0;i --){
        counter[i] = movingFactor & 0xff;
        movingFactor >>= 8;
    }
    
    var tmp2 = totp.stringToHex(counter);
    var result = CryptoJS.HmacSHA1(tmp2,mSeed).toString();
    
    var hash = totp.hexToArr(result);
    var offset = hash[hash.length - 1] & 0xf;
    
    var otpBinary = ((hash[offset] & 0x7f) << 24)
                    |((hash[offset + 1] & 0xff) << 16)
                    |((hash[offset + 2] & 0xff) << 8)
                    |(hash[offset + 3] & 0xff);
    
    var otp = otpBinary % DIGITS_POWER[mOtpLength];
    var result = otp + "";
    
    while(result.length < mOtpLength){
        result = "0" + result;
    }
    
    return result;      
};

totp.hexToArr = function(hex){
    var arr = new Array(parseInt(hex.length/2));
    for(var i = 0;i < arr.length;i ++){
        arr[i] = parseInt(hex.substr(2*i,2),16) & 0xff;
    }
    
    return arr;
};

totp.stringToHex = function(arr){
    var result = "";
    
    var tmp = "";
    for(var i = 0;i < arr.length;i ++){
        tmp = arr[i].toString(16);
        if(tmp.length == 1){
            tmp = "0" + tmp;
        }
        
        result += tmp;
    }
    
    return result;
};
  • totp.js测试代码




Insert title here


    
    
    


2.totp的java实现

import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

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

/**
 * java版totp
 * @author admin
 */
public class Totp {
    
    private static final int[] DIGITS_POWER
    // 0 1  2   3    4     5      6       7        8
    = {1,10,100,1000,10000,100000,1000000,10000000,100000000};
    
    /**
     * 采用默认值3分钟计算比对(GMT时间),60秒产生一个6位otp值
     * @param seed 种子信息
     * @param checkVal 校验值
     * @return
     */
    public static boolean checkOtp(String seed,String checkVal){
        return checkOtp(seed, checkVal, 60, 6);
    }
    
    /**
     * 根据长度创建手机令牌码,长度不要修改
     * @param seed
     * @param date
     * @param length
     * @return
     */
    public static String createOtp(String seed){
        return createOtp(seed, 60, 6);
    }
    
    /**
     * 根据配置的时间计算比对(Asia/Shanhai上海时区)
     * @param seed 种子信息
     * @param checkVal 校验值
     * @param min 客户端和服务端分钟误差
     * @return
     */
    static boolean checkOtp(String seed,String checkVal,int timeStep,int min){
        String tmp = createOtp(seed, timeStep, min);
        if(tmp.equals(checkVal)) return true;
        
        return false;
    }
    
    /**
     * 根据长度创建手机令牌码,长度不要修改
     * @param seed
     * @param date
     * @param length
     * @return
     */
    static String createOtp(String seed,int timeStep, int otpLength){
        Date date = new Date();
        Calendar time = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        time.setTime(date);
        
        long value = time.getTimeInMillis()/1000;
        int movingFactor = (int) (value/timeStep);
        return generateOtp(seed, movingFactor, otpLength);
    }
    
    static byte[] stringToHex(String hexInputString){
        
        byte[] bts = new byte[hexInputString.length() / 2];
        
        for (int i = 0; i < bts.length; i++) {
            bts[i] = (byte) Integer.parseInt(hexInputString.substring(2*i, 2*i+2), 16);
        }
        
        return bts;
    }

    static String generateOtp(String mSeed,long time,int mOtpLength) {
        
        byte[] counter = new byte[8];
        long movingFactor = time;
        
        for(int i = counter.length - 1; i >= 0; i--){
            counter[i] = (byte)(movingFactor & 0xff);
            movingFactor >>= 8;
        }
        
        byte[] hash = hmacSha(mSeed.getBytes(), byteArrayToHexString(counter).getBytes());
        
        int offset = hash[hash.length - 1] & 0xf;
        
        int otpBinary = ((hash[offset] & 0x7f) << 24)
                        |((hash[offset + 1] & 0xff) << 16)
                        |((hash[offset + 2] & 0xff) << 8)
                        |(hash[offset + 3] & 0xff);
        
        int otp = otpBinary % DIGITS_POWER[mOtpLength];
        String result = Integer.toString(otp);
        
        
        while(result.length() < mOtpLength){
            result = "0" + result;
        }
        
        return result;      
    }

    static byte[] hmacSha(byte[] seed, byte[] counter) {
        
        try{
            Mac hmacSha1;
            
            try{
                hmacSha1 = Mac.getInstance("HmacSHA1");
            }catch(NoSuchAlgorithmException ex){
                hmacSha1 = Mac.getInstance("HMAC-SHA-1");
            }
            
            SecretKeySpec macKey = new SecretKeySpec(seed, "RAW");
            hmacSha1.init(macKey);
            
            return hmacSha1.doFinal(counter);
            
        }catch(GeneralSecurityException ex){
            throw new RuntimeException(ex);
        }
    }
    
    static String byteArrayToHexString(byte[] digest) {
        
        StringBuffer buffer = new StringBuffer();
        
        for(int i =0; i < digest.length; i++){
            String hex = Integer.toHexString(0xff & digest[i]);
            
            if(hex.length() == 1)
                buffer.append("0");
            
            buffer.append(hex);

        }
        
        return buffer.toString();
    }

    public static void main(String[] args) throws Exception {
        String seed = "3132333435363738393031323334353637383930";
        System.out.println(createOtp(seed));
        System.out.println(checkOtp(seed,"527078"));
    }
    
}

四.HOTP原理类似

后面会给出hotp

你可能感兴趣的:(手机动态口令)