动态密码代码类记录

前言

动态密码,也可以称为一次性密码(One Time Password,OTP),大部分情况下都是可以随着一个变量(如时间,使用次数等等因素)的变化而重新生成的密码,在我们的工作生活中经常可以碰到。下面我们对动态密码的简单生成做一个说明。

一、基本算法介绍

动态密码的基本认证原理

动态密码的基本认证原理是认证的双方共享种子秘钥。
认证的双方使用同一个种子密码对一个事件选定指定的算法进行计算,这样才能够完成正常的认证工作。

常见的动态密码算法

一般来说,常见的动态密码有两类:

  • 计次使用(HOTP):计次使用的OTP生成之后,可在不限时间内使用,每次成功的使用OTP之后,计数器加1,生成新的密码。用于实现计次使用动态密码的算法叫 HOTP。

  • 计时使用(TOTP):计时使用的OTP则可设定密码有效时间,超时之后则旧OTP废弃不用,需要再次生成新密码。用于实现计时使用动态密码的算法叫TOTP。

HOTP

HOTP 算法,全称是“An HMAC-Based One-Time Password Algorithm”,是一种基于事件计数的一次性密码生成算法。

算法本身可以用两条简短的表达式描述:

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
PWD(K,C,digit) = HOTP(K,C) mod 10^Digit

详细的算法原理可以参考:动态密码算法介绍与实现,本文不再赘述。

TOTP

TOTP 算法,全称是 TOTP: Time-Based One-Time Password Algorithm,其基于 HOTP 算法实现,核心是将移动因子从 HOTP 中的事件计数改为时间差。完整的 TOTP 算法的说明可以查看 RFC 6238,其公式描述也非常简单:

TOTP = HOTP(K, T) // T is an integer
and represents the number of time steps between the initial counter
time T0 and the current Unix time

More specifically, T = (Current Unix time - T0) / X, where the
default floor function is used in the computation.

详细的算法原理可以参考:动态密码算法介绍与实现,本文不再赘述。

二、代码实现

代码如下:

public class TOTPUtil {

    private static final String SECRET_KEY = "def9494ba29d2ccf021cc08003300e4c";

    /**
     * 时间步长 单位:毫秒 作为口令变化的时间周期
     */
    private static final long STEP = 24 * 60 * 60 * 1000;

    /**
     * 转码位数 [1-8]
     */
    private static final int CODE_DIGITS = 6;

    /**
     * 初始化时间
     */
    private static final long INITIAL_TIME = 0;

    /**
     * 柔性时间回溯
     */
    private static final long FLEXIBILIT_TIME = 5000;

    /**
     * 数子量级
     */
    private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};

    private static final String code = "Admin";
    private static final String pass = "PASS";

    /**
     * 生成默认的一次性密码
     *
     * @return
     */
    public static String generateMyTOTP() {
        return generateMyTOTP(code, pass);
    }

    /**
     * 生成一次性密码
     *
     * @param code 账户
     * @param pass 密码
     * @return String
     */
    public static String generateMyTOTP(String code, String pass) {
        if (TextUtils.isEmpty(code) || TextUtils.isEmpty(pass)) {
            Logger.get().e("password is null");
        }
        long now = new Date().getTime();
        //返回为无符号整数基数为16的整数参数的字符串
        String time = Long.toHexString(timeFactor(now)).toUpperCase();
        return generateTOTP(code + pass + SECRET_KEY, time);
    }

    /**
     * 获取动态因子
     *
     * @param targetTime 指定时间
     * @return long
     */
    private static long timeFactor(long targetTime) {
        return (targetTime - INITIAL_TIME) / STEP;
    }

    /**
     * 哈希加密
     *
     * @param crypto   加密算法
     * @param keyBytes 密钥数组
     * @param text     加密内容
     * @return byte[]
     */
    private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
        try {
            Mac hmac;
            hmac = Mac.getInstance(crypto);
            SecretKeySpec macKey = new SecretKeySpec(keyBytes, "AES");
            hmac.init(macKey);
            return hmac.doFinal(text);
        } catch (GeneralSecurityException gse) {
            throw new UndeclaredThrowableException(gse);
        }
    }

    private static byte[] hexStr2Bytes(String hex) {
        byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
        byte[] ret = new byte[bArray.length - 1];
        System.arraycopy(bArray, 1, ret, 0, ret.length);
        return ret;
    }

    private static String generateTOTP(String key, String time) {
        return generateTOTP(key, time, "HmacSHA1");
    }

    private static String generateTOTP256(String key, String time) {
        return generateTOTP(key, time, "HmacSHA256");
    }

    private static String generateTOTP512(String key, String time) {
        return generateTOTP(key, time, "HmacSHA512");
    }

    private static String generateTOTP(String key, String time, String crypto) {
        StringBuilder timeBuilder = new StringBuilder(time);
        while (timeBuilder.length() < 16)
            timeBuilder.insert(0, "0");
        time = timeBuilder.toString();
        byte[] msg = hexStr2Bytes(time);
        byte[] k = key.getBytes();
        byte[] hash = hmac_sha(crypto, k, msg);
        return truncate(hash);
    }

    /**
     * 截断函数
     *
     * @param target 20字节的字符串
     * @return String
     */
    private static String truncate(byte[] target) {
        StringBuilder result;
        int offset = target[target.length - 1] & 0xf;
        int binary = ((target[offset] & 0x7f) << 24)
                | ((target[offset + 1] & 0xff) << 16)
                | ((target[offset + 2] & 0xff) << 8) | (target[offset + 3] & 0xff);

        int otp = binary % DIGITS_POWER[CODE_DIGITS];
        result = new StringBuilder(Integer.toString(otp));
        while (result.length() < CODE_DIGITS) {
            result.insert(0, "0");
        }
        return result.toString();
    }

    /**
     * 刚性口令验证
     *
     * @param code 账户
     * @param pass 密码
     * @param totp 待验证的口令
     * @return boolean
     */
    public static boolean verifyTOTPRigidity(String code, String pass, String totp) {
        return generateMyTOTP(code, pass).equals(totp);
    }

    /**
     * 柔性口令验证
     *
     * @param code 账户
     * @param pass 密码
     * @param totp 待验证的口令
     * @return boolean
     */
    public static boolean verifyTOTPFlexibility(String code, String pass, String totp) {
        long now = new Date().getTime();
        String time = Long.toHexString(timeFactor(now)).toUpperCase();
        String tempTotp = generateTOTP(code + pass + SECRET_KEY, time);
        if (tempTotp.equals(totp)) {
            return true;
        }
        String time2 = Long.toHexString(timeFactor(now - FLEXIBILIT_TIME)).toUpperCase();
        String tempTotp2 = generateTOTP(code + pass + SECRET_KEY, time2);
        return tempTotp2.equals(totp);
    }
}

参考文章

动态密码算法介绍与实现

动态密码实现

你可能感兴趣的:(动态密码代码类记录)