📜  一次性密码(1)

📅  最后修改于: 2023-12-03 15:06:10.097000             🧑  作者: Mango

一次性密码

什么是一次性密码?

一次性密码又称作动态密码或者单次密码,是指一种只能使用一次,用于身份验证的密码。它被广泛应用于银行、网络安全等领域。

一次性密码的原理

一次性密码的实现原理通常为:通过生成算法,将一个固定长度的口令转变为另一个口令,每个口令只能在一个特定的时间内使用,超过时间之后便会失效。因此,攻击者需要在非常短的时间内(一般为几十秒)内,在正确的时间内获取到一次性密码才能实现进入系统或做恶意操作。如此一来,一次性密码具有非常高的安全性。

一次性密码的使用

一次性密码通常分为两种类型:基于时间的一次性密码(TOTP)和基于计数器的一次性密码(HOTP)。

TOTP

基于时间的一次性密码根据时间戳产生动态密码。它的工作流程如下:

  1. 客户端使用预共享的密钥计算当前时间戳的哈希值。
  2. 利用哈希值获取动态密码。
  3. 服务端计算当前时间戳的哈希值,并将其与客户端提交的密码进行比较,以核实客户端的身份。

常见的TOTP算法有HMAC-SHA1、HMAC-SHA256、HMAC-SHA512、HMAC-MD5等。

HOTP

基于计数器的一次性密码则是根据事件序列号产生动态密码。它的工作流程如下:

  1. 客户端和服务端都需要保存一个计数器。
  2. 客户端使用预共享的密钥以及当前计数器的值计算哈希值。
  3. 根据哈希值获取动态密码。
  4. 客户端的计数器自增。
  5. 服务端接收到客户端提交的哈希值后,对比哈希值的有效性,并更新服务端的计数器。

常见的HOTP算法有HMAC-SHA1、HMAC-SHA256、HMAC-SHA512、HMAC-MD5等。

一次性密码的优缺点
优点
  1. 高度安全性,恶意攻击者难以将其盗用。
  2. 简单易用,一次性密码只需要通过算法生成即可,非常方便使用。
缺点
  1. 需要客户端与服务端共同支持一次性密码算法。
  2. 密钥的传递和管理可能存在风险。
  3. 一次性密码的有效期限制可能会带来一定的不便。
一次性密码的实现

以下是Java代码实现一次性密码生成算法:

public class OTPUtils {
    private static final long timeStep = 30000; // 时间步长
    private static final int pwdLength = 6; // 密码长度

    /**
     * TOTP算法生成动态密码
     * @param key 密钥
     * @param now 当前时间
     * @param t 键值
     * @return 动态密码
     */
    public static String getTOTP(byte[] key, long now, long t) {
        long count = now / timeStep;
        String hexCount = Long.toHexString(count);
        while (hexCount.length() < 16) {
            hexCount = "0" + hexCount;
        }

        byte[] bytes = hexStr2Bytes(hexCount);
        byte[] hash = hmacSha1(key, bytes);
        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 pwd = binary % (int) Math.pow(10, pwdLength);
        return String.format("%06d", pwd);
    }

    /**
     * 将16进制字符串转换为字节数组
     * @param hexStr 16进制字符串
     * @return 字节数组
     */
    private static byte[] hexStr2Bytes(String hexStr) {
        byte[] bytes = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length(); i += 2) {
            String subStr = hexStr.substring(i, i + 2);
            bytes[i / 2] = (byte) Integer.parseInt(subStr, 16);
        }
        return bytes;
    }

    /**
     * HMAC-SHA1散列函数
     * @param key 密钥
     * @param data 数据
     * @return 散列结果
     */
    private static byte[] hmacSha1(byte[] key, byte[] data) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(secretKeySpec);
            return mac.doFinal(data);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }
}
参考文献