美文网首页
手机动态口令

手机动态口令

作者: 小诸葛686 | 来源:发表于2018-11-15 21:57 被阅读0次

    一.什么是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测试代码
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        <script type="text/javascript" src="js/hmac-sha1.js"></script>
        <script type="text/javascript" src="js/totp.js"></script>
        <script type="text/javascript">
            //第一个参数密钥,第二个参数时间间隔60秒,第三个参数otp长度6位
            var otp = totp.generateOtp("3132333435363738393031323334353637383930",60,6);
            console.log(otp);
        </script>
    </body>
    </html>
    

    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

    相关文章

      网友评论

          本文标题:手机动态口令

          本文链接:https://www.haomeiwen.com/subject/lpyufqtx.html