精华内容
下载资源
问答
  • hotp
    2021-07-05 13:55:16

    HOTP的工作原理

    HTOP(K,C) = Truncate(HMAC-SHA-1(K,C))
    客户端和服务器事先协商好一个密钥K,用于一次性密码的生成过程。此外,客户端和服务器各有一个计数器C,并且事先将计数值同步。而Truncate是为了获得一个符合HTOP要求的值。

    TOTP的工作原理

    Time-based One-time Password (TOTP):即基于时间的一次性密码算法,也称时间同步的动态密码。
    TOTP = Truncate(HMAC-SHA-1(K,T))
    TOTP是HOTP的一个变种,将HOTP中的计数器C替换为依托时间的参数T,T是由当前时间(CurrentUnixTime、初始时间(T0)、步长(X)决定的。

    即:T = (Current Unix time – T0) / X

    • CurrentUnixTime:当前的Unix时间。
    • T0: 开始计步初始化时间,默认为0
    • X : 步长,默认情况下为30s

    TOTP的一些要求

    • 客户端和服务器必须能够彼此知道或者推算出对方的Unix Time
    • 客户端和服务器端必须共享一个密钥
    • 算法必须使用HOTP作为其关键实现环节
    • 客户端和服务器端必须使用相同的步长X
    • 每一个客户端必须拥有不同的密钥
    • 密钥的生成必须足够随机
    • 密钥必须储存在防篡改的设备上,而且不能在不安全的情况下被访问或使用。
    • 对该算法中T的实现必须大于int32,因为它在2038年将超出上限。
    • T0和X的协商必须在之前的步骤中就已经做好了。

    原理摘抄于  https://www.sooele.com/1529.html

        <dependency>
                <groupId>com.eatthepath</groupId>
                <artifactId>java-otp</artifactId>
                <version>0.2.0</version>
            </dependency>
    	@Test
    	void testHotp() throws DecoderException, InvalidKeyException, NoSuchAlgorithmException {
    		HmacOneTimePasswordGenerator hmacOneTimePasswordGenerator = new HmacOneTimePasswordGenerator(8);
    		String key = "123456";
    		byte[] binaryKey = Hex.decodeHex(key.toCharArray());
    		SecretKeySpec secretKeySpec = 	new SecretKeySpec(binaryKey, "HmacSHA1");
    		System.out.println(hmacOneTimePasswordGenerator.generateOneTimePassword(secretKeySpec,12345678));
    	}
    @Test
    	void testTotp() throws DecoderException, InvalidKeyException, NoSuchAlgorithmException {
    		TimeBasedOneTimePasswordGenerator totpGenertor = new TimeBasedOneTimePasswordGenerator(Duration.ofMinutes(1L));
    		final Instant now = Instant.now();
    		String key = "123456";
    		byte[] binaryKey = Hex.decodeHex(key.toCharArray());
    		SecretKeySpec secretKeySpec = 	new SecretKeySpec(binaryKey, "HmacSHA1");
    		System.out.println(totpGenertor.generateOneTimePassword(secretKeySpec,now));
    	}

     

    更多相关内容
  • HOTP和TOTP的Java语言实现 一个小型JavaScript库(最小压缩了17k,最小压缩了6.3k,gzip压缩了),该库和处理生成。根据。 该库产生与Google Authenticator应用相同的代码。 该软件包仅将jsOTP和jsSHA公开给全局...
  • TYPO3扩展名mfa_hotp 此扩展使用从TYPO3 v11.1开始可用的新MFA API将HOTP(基于hmac的一次性密码)MFA提供程序添加到TYPO3。 此外,它可以用作有关如何将自定义提供程序集成到TYPO3中的示例扩展。 注意:由于TYPO3...
  • 主人的现状: 此 repo 包含来自Java 中 HOTP 的参考实现和一些 JUnit 测试用例。 它还包含一个可以扩展的简单非静态版本。
  • 该应用程序支持HOTP(事件令牌, //tools.ietf.org/html/rfc4226)和TOTP令牌(时间令牌, //tools.ietf.org/html/draft-mraihi-totp-timebased- )。 该应用程序支持使用以下资源配置令牌 二维码手动创建。 可以...
  • otpauth-rs用于Rust的HOTP / TOTP的两步验证。 安装将其添加到您的Cargo.toml中:[dependencies] otpauth =“ 0.3”示例HOTP示例u otpauth-rs Rust的HOTP / TOTP的两步验证。 安装将其添加到您的Cargo.toml中:...
  • RFC4226)和基于时间的一次性密码算法(HOTP; RFC6238)的实现。用法要创建新的TOTP机密,请执行以下操作: require "otp"# Create a TOTP instance and new keytotp = OTP::TOTP.newtotp.new_secret # create ...
  • 示例基于Android平台, JAVA通用. HOTP和TOTP动态密码Android示例源码 可用于做动态数字密码相关, 如:密码门禁等
  • HOTP-TOTP.PHP-开源

    2021-05-13 06:14:31
    该工具可以基于HOTP(RFC 4226:HOTP:基于HMAC的一次性密码算法),TOTP(RFC 6238:TOTP:基于时间的一次性密码算法)和OCRA(RFC)创建一次性密码值6287:OCRA:OATH质询-响应算法)标准,并且还支持OAuth协议...
  • jsotp - 基于RFC4226(HOTP)生成和验证一次性密码的node模块
  • HOTP-Node.js-示例服务器 关于模式 HOTP(基于 HMAC 的一次性密码)操作的概念证明
  • 使用简单、快速且零依赖。... 它可以与结合使用,后者具有适用于 iOS、Android 和 BlackBerry 的免费应用程序。 安装 npm install notp 用法 var notp = require ( 'notp' ) ;...// Check TOTP is correct (HOTP if h
  • RefCodeGenerator 使用HOTP算法的RefCodeGenerator
  • 旨在提供 RFC 指定的 HOTP、TOTP 和 OCRA 的实现 Implemented: HOTP (RFC 4226) TOTP (RFC 6238) OCRA (RFC 6287)
  • 动态口令HOTP的JAVA代码实现,通过种子和认证基数产生6个字符的动态口令
  • 1kB压缩 我在TypeScript + base32编码器中实现2FA H / TOTP算法,用于为器之类的验证器程序创建链接有关otpauth://链接的信息规格: HOTP- RFC 4226 TOTP- RFC 6238 Base32- RFC 4648 (无需填充,感谢@LinusU ) ...
  • OATH-HOTP和OATH-TOTP的简单实现。 它根据MIT许可证的条款分发。 描述 这是纯lua中的简单OATH-HOTP和OATH-TOTP实现,利用LuaCrypto满足其哈希需求。 它可用作生成和验证库,并且与RFC 4226和6238兼容。它仅支持SHA-...
  • HOTP参数 TOTP参数 支持的运营 生成HOTP和TOTP代码。 验证HOTP和TOTP代码。 将OTP配置导出为Google Authenticator URI 。 将OTP配置导出为QR码图像(用于在身份验证器应用中注册机密)。 将OTP配置导出为JSON。...
  • 一次性密码适用于iOS的TOTP和HOTP一次性密码 OneTimePassword库是的核心。 它可以生成和标准化的一次性密码。 它还可以读取并生成通常用于设置OTP令牌的 ,并且可以将令牌保存到iOS安全钥匙串和从iOS安全钥匙串加载...
  • PHP中的TOTP / HOTP库 一个PHP库,用于根据 (HOTP算法)和 (TOTP算法)生成一次性密码该库与可用于Android和iPhone的Google Authenticator应用程序兼容。 它也与其他应用程序兼容,例如 。文献资料该项目的位于。...
  • oathgen 的目标是完整、标准和便携。 Oathgen 是用 C++11 编写的。 为了构建 oathgen,您必须使用 C++11 编译器。 有关确切的编译器选项,请参阅build.sh脚本。 Oathgen 已经构建在 g++、clang++ 和 MSVC++ 编译器...
  • rust-oath该库旨在提供RFC规定的HOTP,TOTP和OCRA的实现。 实现:HOTP(RFC 4226)TOTP(RFC 6238)rust-oath该库旨在提供RFC规定的HOTP,TOTP和OCRA的实现。 实现:HOTP(RFC 4226)TOTP(RFC 6238)OCRA(RFC 6287...
  • 一次性密码HOTP/TOTP

    千次阅读 2019-10-28 22:42:04
    public static string HOTP(byte[] key, byte[] counter, int length = 6) { using (var hashAlgorithm = new HMACSHA1(key)) { var hmac = hashAlgorithm.ComputeHash(counter); var offset = hmac[hmac....

    OTP一次性密码
    OTP是One Time Password的简写,即一次性密码。在平时生活中,我们接触一次性密码的场景非常多,比如在登录账号、找回密码,更改密码和转账操作等等这些场景,其中一些常用到的方式有:

    手机短信+短信验证码;

    邮件+邮件验证码;

    认证器软件+验证码,比如Microsoft Authenticator App,Google Authenticator App等等;

    硬件+验证码:比如网银的电子密码器;

    这些场景的流程一般都是在用户提供了账号+密码的基础上,让用户再提供一个一次性的验证码来提供一层额外的安全防护。通常情况下,这个验证码是一个6-8位的数字,只能使用一次或者仅在很短的时间内可用(比如5分钟以内)。

    HOTP基于消息认证码的一次性密码
    HOTP是HMAC-Based One Time Password的缩写,即是基于HMAC(基于Hash的消息认证码)实现的一次性密码。算法细节定义在RFC4226(https://tools.ietf.org/html/rfc4226),算法公式为: HOTP(Key,Counter)  ,拆开是 Truncate(HMAC-SHA-1(Key,Counter)) 。

    Key:密钥;

    Counter:一个计数器;

    HMAC-SHA-1:基于SHA1的HMAC算法的一个函数,返回MAC的值,MAC是一个20bytes(160bits)的字节数组;

    Truncate:一个截取数字的函数,以3中的MAC为参数,按照指定规则,得到一个6位或者8位数字

     

    TOTP基于时间的一次性密码
    TOTP是Time-Based One Time Password的缩写。TOTP是在HOTP的基础上扩展的一个算法,算法细节定义在RFC6238(https://tools.ietf.org/html/rfc6238),其核心在于把HOTP中的counter换成了时间T,可以简单的理解为一个当前时间的时间戳(unixtime)。一般实际应用中会固定一个时间的步长,比如30秒,60秒,120秒等等,也就是说再这个步长的时间内,基于TOTP算法算出的OTP值是一样的。

     

    举例:

    Nuget:

    System.Security.Cryptography.Algorithms

    Rfc6238AuthenticationService.cs 

    来自:https://github.com/aspnet/AspNetCore  AspNetCore/src/Identity/Extensions.Core/src/Rfc6238AuthenticationService.cs

    // Copyright (c) .NET Foundation. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
    
    using System.Diagnostics;
    using System.Net;
    using System.Security.Cryptography;
    
    namespace OTP
    {
        using System;
        using System.Text;
    
        internal static class Rfc6238AuthenticationService
        {
            private static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            private static readonly TimeSpan _timestep = TimeSpan.FromMinutes(3);
            private static readonly Encoding _encoding = new UTF8Encoding(false, true);
            private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
    
            // Generates a new 80-bit security token
            public static byte[] GenerateRandomKey()
            {
                byte[] bytes = new byte[20];
                _rng.GetBytes(bytes);
                return bytes;
            }
    
            internal static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier)
            {
                // # of 0's = length of pin
                const int Mod = 1000000;
    
                // See https://tools.ietf.org/html/rfc4226
                // We can add an optional modifier
                var timestepAsBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)timestepNumber));
                var hash = hashAlgorithm.ComputeHash(ApplyModifier(timestepAsBytes, modifier));
    
                // Generate DT string
                var offset = hash[hash.Length - 1] & 0xf;
                Debug.Assert(offset + 4 < hash.Length);
                var binaryCode = (hash[offset] & 0x7f) << 24
                                 | (hash[offset + 1] & 0xff) << 16
                                 | (hash[offset + 2] & 0xff) << 8
                                 | hash[offset + 3] & 0xff;
    
                return binaryCode % Mod;
            }
    
            private static byte[] ApplyModifier(byte[] input, string modifier)
            {
                if (string.IsNullOrEmpty(modifier))
                {
                    return input;
                }
    
                var modifierBytes = _encoding.GetBytes(modifier);
                var combined = new byte[checked(input.Length + modifierBytes.Length)];
                Buffer.BlockCopy(input, 0, combined, 0, input.Length);
                Buffer.BlockCopy(modifierBytes, 0, combined, input.Length, modifierBytes.Length);
                return combined;
            }
    
            // More info: https://tools.ietf.org/html/rfc6238#section-4
            private static ulong GetCurrentTimeStepNumber()
            {
                var delta = DateTime.UtcNow - _unixEpoch;
                return (ulong)(delta.Ticks / _timestep.Ticks);
            }
    
            public static int GenerateCode(byte[] securityToken, string modifier = null)
            {
                if (securityToken == null)
                {
                    throw new ArgumentNullException(nameof(securityToken));
                }
    
                // Allow a variance of no greater than 9 minutes in either direction
                var currentTimeStep = GetCurrentTimeStepNumber();
                using (var hashAlgorithm = new HMACSHA1(securityToken))
                {
                    return ComputeTotp(hashAlgorithm, currentTimeStep, modifier);
                }
            }
    
            public static bool ValidateCode(byte[] securityToken, int code, string modifier = null)
            {
                if (securityToken == null)
                {
                    throw new ArgumentNullException(nameof(securityToken));
                }
    
                // Allow a variance of no greater than 9 minutes in either direction
                var currentTimeStep = GetCurrentTimeStepNumber();
                using (var hashAlgorithm = new HMACSHA1(securityToken))
                {
                    for (var i = -2; i <= 2; i++)
                    {
                        var computedTotp = ComputeTotp(hashAlgorithm, (ulong)((long)currentTimeStep + i), modifier);
                        if (computedTotp == code)
                        {
                            return true;
                        }
                    }
                }
    
                // No match
                return false;
            }
        }
    }

    Program.cs

    using System;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading;
    
    namespace OTP
    {
        class Program
        {
            static void Main(string[] args)
            {
                var code = Rfc6238AuthenticationService.GenerateCode(UTF8Encoding.UTF8.GetBytes("nii"));
                var validateResult = Rfc6238AuthenticationService.ValidateCode(UTF8Encoding.UTF8.GetBytes("nii"), code);
    
    
                //HOTP基于消息认证码的一次性密码
                //密钥key
                var key = UTF8Encoding.UTF8.GetBytes("xx_key");
                //计数器
                var counter = UTF8Encoding.UTF8.GetBytes("xx_counter");
                //otp6=068878
                var otp6 = HOTP(key, counter, 6);
                var otp5 = HOTP(key, counter, 6);
                //otp8=33068878
                var otp8 = HOTP(key, counter, 8);
    
                //TOTP基于时间的一次性密码
                //密钥keyvar
                key = UTF8Encoding.UTF8.GetBytes("xx_key");
                //在10秒内生成,otp是一样的
                for (var i = 0; i < 100000; i++)
                {
                    var otp = TOTP(key, 10, 6);
                    Console.WriteLine(otp);
                    Thread.Sleep(1000);
                }
    
                Console.WriteLine("Hello World!");
    
            }
    
            /// <summary>
            /// HOTP基于消息认证码的一次性密码
            /// </summary>
            /// <param name="key"></param>
            /// <param name="counter"></param>
            /// <param name="length"></param>
            /// <returns></returns>
            public static string HOTP(byte[] key, byte[] counter, int length = 6)
            {
                using (var hashAlgorithm = new HMACSHA1(key))
                {
                    var hmac = hashAlgorithm.ComputeHash(counter);
    
                    var offset = hmac[hmac.Length - 1] & 0xF;
    
                    var b1 = (hmac[offset] & 0x7F) << 24;
                    var b2 = (hmac[offset + 1] & 0xFF) << 16;
                    var b3 = (hmac[offset + 2] & 0xFF) << 8;
                    var b4 = (hmac[offset + 3] & 0xFF);
    
                    var code = b1 | b2 | b3 | b4;
    
                    var value = code % (int)Math.Pow(10, length);
    
                    return value.ToString().PadLeft(length, '0');
                }
            }
    
            /// <summary>
            /// TOTP基于时间的一次性密码
            /// </summary>
            /// <param name="key"></param>
            /// <param name="step"></param>
            /// <param name="length"></param>
            /// <returns></returns>
            public static string TOTP(byte[] key, int step = 60, int length = 6)
            {
                var unixTime = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
                var counter = ((int)unixTime) / step;
                var counterBytes = BitConverter.GetBytes(counter);
                return HOTP(key, counterBytes, length);
            }
        }
    }
    

     


     

     

    展开全文
  • 本文根据 RFC4226 和 RFC6238 文档,详细的介绍 HOTP 和 TOTP 算法的原理和实现。 两步验证已经被广泛应用于各种互联网应用当中,用来提供安全性。 对于如何使用两步验证,大家并不陌生,无非是开启两步验证,然后...

    1、简介

    本文根据 RFC4226 和 RFC6238 文档,详细的介绍 HOTP 和 TOTP 算法的原理和实现。

    两步验证已经被广泛应用于各种互联网应用当中,用来提供安全性。

    对于如何使用两步验证,大家并不陌生,无非是开启两步验证,然后出现一个二维码,使用支持两步验证的移动应用比如 Google Authenticator 或者 LassPass Authenticator 扫一下二维码。

    这时候应用会出现一个 6 位数的一次性密码,首次需要输入验证从而完成开启过程。

    以后在登陆的时候,除了输入用户名和密码外,还需要把当前的移动应用上显示的 6 位数编码输入才能完成登陆。

    这个过程的背后主要由两个算法来支撑:HOTP 和 TOTP。

    分别对应着两份 RFC 协议 RFC4266 和 RFC6238。

    前者是 HOTP 的标准,后者是 TOTP 的标准。

    本文将使用图文并茂的方式详细介绍 HOTP 和 TOTP 的算法原理,并在最后分析其安全性。

    当然所有内容都是基于协议的,通过自己的理解更加直观的表达出来。

    2、协议解决的核心问题

    通过前面两步验证的使用场景分析,不难看出问题的核心在于如何能够让用户手机应用产生的验证码和服务器产生的验证码一致,或者是在一定范围内一致。

    参考下图:

    所以我们的算法就是在解决如何更好的生成这个验证码,既能保证服务器端和客户端同步,还能保证验证码不重复并且不容易被别人反向破解出共享密钥。

    其中如果是计数,则是 HOTP,如果是使用时间来生成验证码,则是 TOTP。

    3、HOTP 算法图解

    3.1、符号定义

    对于 HOTP,通过上图我们已经看到输入算法的主要有两个元素,一个是共享密钥,另外一个是计数。

    在 RFC 算法中用一下字母表示:

    K 共享密钥,这个密钥的要求是每个 HOTP 的生成器都必须是唯一的,一般我们都是通过一些随机生成种子的库来实现。

    C 计数器,RFC 中把它称为移动元素(moving factor)是一个 8 个 byte 的数值,而且需要服务器和客户端同步。

    另外一个参数比较好理解,Digit 表示产生的验证码的位数。

    T 称为限制参数(Throttling Parameter)表示当用户尝试 T 次 OTP 授权后不成功将拒绝该用户的连接。

    s 称为重新同步参数(Resynchronization Parameter)表示服务器将通过累加计数器,来尝试多次验证输入的一次性密码,而这个尝试的次数及为 s。

    该参数主要是有效的容忍用户在客户端无意中生成了额外不用于验证的验证码,导致客户端和服务端不一致,但同时也限制了用户无限制的生成不用于验证的一次性密码。

    3.2、算法流程

    核心步骤主要是使用 K、C 和 Digit。

    第一步:使用 HMAC-SHA-1 算法基于 K 和 C 生成一个20个字节的十六进制字符串(HS)。

    关于如何生成这个是另外一个协议来规定的,RFC 2104 HMAC Keyed-Hashing for Message Authentication. 实际上这里的算法并不唯一,还可以使用 HMAC-SHA-256 和 HMAC-SHA-512 生成更长的序列。

    对应到协议中的算法标识就是:HS = HMAC-SHA-1(K,C)

    第二步:选择这个20个字节的十六进制字符串(HS 下文使用 HS 代替 )的最后一个字节,取其低4位数并转化为十进制。

    比如图中的例子,第二个字节是 5a,第四位就是 a,十六进制也就是 0xa,转化为十进制就是 10。

    该数字我们定义为 Offset,也就是偏移量。

    第三步:根据偏移量 Offset,我们从 HS 中的第 10(偏移量)个字节开始选取 4 个字节,作为我们生成 OTP 的基础数据。

    图中例子就是选择 50ef7f19,十六进制表示就是 0x50ef7f19,我们成为 Sbits
    以上两步在协议中的成为 Dynamic Truncation (DT)算法。

    具体参考以下伪代码:

    Let Sbits = DT(HS)  // DT, defined below, 
                        // returns a 31-bit string

    展开就是:

    DT(String) // String = String[0]...String[19]
        Let OffsetBits be the low-order 4 bits of String[19] 
        Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15 
        Let P = String[OffSet]...String[OffSet+3] 
        Return the Last 31 bits of P

    第四步:将上一步4个字节的十六进制字符串 Sbits 转化为十进制,然后用该十进制数对 10 的 Digit 次幂 进行取模运算。

    其原理很简单根据取模运算的性质,比如:比10大的数 MOD 10 结果必然是 0到9,MOD 100 结果必然是 0-99。

    图中的例子,50ef7f19 转化为十进制为 1357872921,然后如果需要6位 OTP 验证码,则 1357872921 MOD 10^6 = 872921。 

    872921 就是我们最终生成的 OTP。

    对应到协议中的算法为:

    Let Snum = StToNum(Sbits) // Convert S to a number in
                              // 0...2^{31}-1 
    
    Return D = Snum mod 10^Digit // D is a number in the range
                                 // 0...10^{Digit}-1

    这一步可能还需要注意一点就是图中案例 Digit 不能超过 10,因为即使超过 10,
    1357872921 取模后也不会超过10位了。

    所以如果我们希望获取更长的验证码,需要在三步中拿到更多的十六进制字节,从而得到更大的十进制数。

    这个十进制决定了生成的 OTP 编码的长度。

    4、TOTP 算法图解

    在 HOTP 算法的基础上,对于 TOTP 算法的解释是不难了,因为 TOTP 实际上是基础 HOTP 的,只不过 HOTP 的计数器在 TOTP 中不再是直接的计数器了,而是使用时间来简介计数的。

    下图将会详细介绍 TOTP 是如何在 HOTP 基础上使用时间来计数的。

    4.1、符号定义

    时间窗口 X :表示多长时间算作计数器的一步,通常会设为30秒;

    初始计数时间戳 $T_0$:使用 Unix 时间戳来表示 OTP 生成的时候的初始计数时间。

    4.2、算法流程

    TOTP 算法的关键在于如何更具当前时间和时间窗口计算出计数,也就是如何根据当前时间和 X 来计算 HOTP 算法中的 C。

    HOTP 算法中的 C 是使用当前 Unix 时间戳减去初始计数时间戳,然后除以时间窗口而获得的。

    5、算法安全性分析

    上一节我们的算法中有两个参数没有用 T 和 s。这两个参数对安全有重要的作用。

    官方协议在这里给出了5点安全要求,其中第一点是协议本身的要求,理论上进行约束,我们主要关心另外4点,分别是 HOTP 的验证,限制访问参数,重新同步参数以及共享密钥的管理。

    对于二步验证的安全问题实际上就是如何保证第二步验证尽可能不被攻击的前提下向用户提供更方便的服务。
    通过下图我们可以详细的了解 HOTP 的验证过程,同时还可以了解参数 s 和 T 的用途。

    如果用户严格按照生成一次 OTP,然后验证一次的话,服务器直接可以验证成功。因为算法将会输入相同的参数。

    如果用户无意间多生成了若干次 OTP 但是没有用来验证,服务器和客户端就产生差异,这时候服务器端会自动累积计数器来尝试匹配用户输入的 OTP,但是这种尝试是有限制的,这也就是前面说到的参数 s 的作用。

    一旦服务器尝试 s 次仍未匹配成功,那么就会通知客户端需要重新生成验证来验证,只要验证成功。

    协议中对于参数 s 给出的建议是:

    服务器建议设置该参数,但是在保证可用性的前提下,尽可能小。

    另外还可以要求用户输入一个 HOTP 序列(比如连续生成多个 OTP 发送到服务器进行验证)来进行重新同步计数器。

    既然涉及到重试,服务器同样对重试次数有所限制,从而防止暴力破解。

    这里当用户重试次数超过了阈值 T,服务器就应该将该账号标记为有安全风险,需要提醒用户。

    协议中对个参数 T 给出的建议是:

    同样 T 也不建议设的很大。另外还提供了另外一个基于延时的策略还防止暴力破解攻击,服务器设置一个惩罚时间,一旦用户验证失败,需要在惩罚时间乘以失败次数的时间内禁止用户重新尝试验证。

    这个延时需要跨不同的登陆会话,以防止并发的攻击。

     

     


    refer:

    https://www.jianshu.com/p/a7b900e8e50a


     

    展开全文
  • 用C#实现TOTP 和HOTP 。 这是的端口,用于支持.NET Core。 在NuGet上获取 PM> Install-Package Otp.NET 文献资料 TOTP(一次性密码) TOTP是一种算法,它使用时间滚动窗口来计算一次性密码。 它通常用于两因素...
  • 7.2HOTP值的确认 HOTP客户端(软件或硬件令牌)将其计数器值加1后计算出HOTP客户端的下一个HOTP值。如果HOTP服务器接收到的值客户端计算的值一致,则该HOTP值通过认证。在这种情况下,服务器计数器的值加1. 如果...

    摘要

    本文描述了一种基于HMAC的一次性口令生成算法。对该算法进行了安全性分析,并讨论了该算法安全使用的重要参数。该算法已广泛应用于VPN访问,Wi-Fi登录及面向交易的Web应用等多项网络服务。

    本文由OATH组织成员合作完成,详细讨论了可在技术界自由传播的算法。作者相信一种通用且共享的算法可以通过商业或开放资源工具实现共享,从而为Interne上采用的二因子认证提供了便利。

    1.概述

    本文首先介绍了可以生成基于HMAC的一次性口令值的算法的背景,因而这种算法也称为基于HMAC的一次性口令算法。将在第4节列出这种算法的要求,在第5节说明HOTP算法,第6、7节主要对这种算法进行安全性分析,第8节对其进行扩充和完善,并在第10节对本文作出结论。在附录A,有兴趣的读者会发现对算法安全性的详细、全面分析:并对该算法的理想化版本进行了评价,之后对HOTP算法的安全性进行了分析。

    2.引言

    今天,双因子认证仍只是在极有限的范围和规模内得到应用。尽管威胁和攻击的水平在不断提高,大多数Internet应用薄弱的认证体系来监视用户访问。硬件和软件技术供应商之间缺少可操作性成为了双因子认证应用的制约因素。特别是由于缺乏统一规范,硬件和软件组件常常通过专有技术紧密的结合,从而导致高成本解决方案,不佳的采用和有限的创新。

    在过去两年中,网络威胁的迅速增长暴露了Internet上认证主要手段静态口令的不足。同时,目前的方法要求一个终端用户携带一个昂贵的,只能用来进行网络认证的功能单一的装置。这显然也不是最好的方法。对于Internet上流传的双因子认证方法则必须在能够跨广泛应用范围的灵活性更高的装置中嵌入它。

    这种既可以引入基础技术同时又保证广泛互操作性的能力要求其在广大硬件和软件开发者技术界中随时可得。只有一种开放系统方法可以保证基本的原始双因子认证方法可用于下一代用户装置,如USB大量存储器、IP电话以及个人数字助理。

    一次性口令当然是最简单而流行的保护网络访问权的双因子认证方法之一。例如,在大企业中,虚拟专用网络的访问通常都需要使用一次性口令来进行远程用户认证。一次性口令通常更适用于强认证方式如公钥体制(PKI)或生物统计,这是因为空气隔离装置不需要在用户机上安装客户桌面软件,因此可使他们在包括家用电脑、自动售货机、个人数字助理多台机器上漫游。

    本文提出了一种简单的一次性口令算法,可以通过任何硬件制造商或软件开发者实现,从而建立了可互操作认证装置和软件代理。算法是基于事件的,因此它可用于大容量装置如Java智能卡、USB安全装置以及GSM SIM卡。

    所介绍的算法在IETF知识产权[RFC3979]规定的条件下可以在开发界自由使用。

    本文的作者都是OATH组织成员。该组织成立于2004年,其目的是促进强认证技术提供者之间的合作。

    3.术语

    这篇文章中的关键词“必须”、“必须不”、“要求”、“即将”、“将不”、“应该”、“应该不”、“建议”、“可以”、和“可选”都如[RFC2119]中所定义。

    4.算法要求

    这一节说明了进行算法设计的要求。着重强调了终端用户的可用性和算法由低成本硬件实现的能力,从而使用户界面能力最小化。特别是以算法能插入高容量SIM和Java卡的为前提。

    要求1算法必须是基于序列号或基于计数器的。这样的目的之一是可以将HOTP算法用于大容量装置如Java智能卡,USB安全装置和GSM SIM卡。

    要求2算法应该是经济可实现的,硬件上对电池、按键、计算马力、LCD显示器尺寸的需要最小化。

    要求3算法必须用在不支持任何数值型输入的令牌,但也可以用于更复杂的机器如安全PIN键盘。

    要求4令牌上显示的HOTP值必须易于用户阅读和输入,这要求HOTP值必须有一个合理的长度。

    HOTP只必须至少为6位,同时也希望它仅由数字构成,这样用户就很容易在电话这样的有限设备上输入。

    要求5必须有用户——可利用的友好装置来实现计数器重同步,7.4节和附录E.4详细说明了本文提到的重同步装置。

    要求6算法必须使用强共享的密钥,共享密钥的长度必须至少为128比特。本文建议共享密钥的长度为160比特。

    5.HOTP算法

    在这一节,我们介绍标记并描述HOTP算法的基本模块­——计算HMAC-SHA1值的基本函数,以及抽取HOTP值的截短方法。

    5.1标记和符号

    一个数串通常为二进制数串,即0和1的序列。

    如果s是一个数组,则|s|表示其长度。

    如果n是一个数,则|n|表示其绝对值。

    如果s是一个数组,则s[i]表示其第i比特。我们以0为开始对这些比特进行编号。因此s = s[0]s[1]...s[n-1],这里n = |s|是s的长度。

    StToNum表示二进制串输入转换为十进制数的函数。

    下面是本文中用到的符号列表

    ---------------------------------------------------------------

     

    C        8-byte 计数器值,此计数器必须与HOTP生成器(客户端)和HOTP认证器(服务器)保持同步。

    K        客户端和服务器之间的共享密钥;每一个HOTP生成器都有互不相同且唯一的密钥K。

    T        阈值参数:用户尝试认证次数达到T次后,服务器将拒绝该用户接入。

    S        重同步参数:服务器将通过依次的S个计数器值来验证接收到的认证码。

    Digit    系统参数,HOTP值的位数。

    5.2描述

    HOTP算法是基于一个加法计数器和一个静态的对称密钥,该密钥仅有令牌和认证服务器知道。为得到HOTP值,我们使用HMAC-SHA-1算法,如RFC 2104 [BCK2]所定义。

    由于HMAC-SHA-1计算输出为160比特,我们必须将其截短以便于用户输入。

    HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

    这里:

    Truncate表示将HMAC-SHA-1值转换为HOTP值的函数,如5.3节所定义。

    首先对密钥(K),计数器(C),和Data值执行高阶字节散列。

    HOTP生成器产生的HOTP值可以作为大尾数处理。

    5.3.生成HOTP值

    我们可以分为3个步骤来描述改过程

    步骤1:生成一个HMAC-SHA-1值,令HS = HMAC-SHA-1(K,C)// HS是一个20字节的数组。

    步骤2:生成一个4字节数组(动态截短)

    令Sbits = DT(HS)//DT稍后定义

                    //返回31位的数串

    步骤3:计算HOTP值

    令Snum = StToNum(Sbits)//将Sbits转换为10进制数,取值在0值2^{31}-1之间

    返回D = Snum mod 10^Digit//D是一个十进制数,取值在0至10^{digit}-1之间

    截短函数用于步骤2和步骤3中,即进行动态截短之后与10^{digit}进行模运算。动态截短函数的作用是从160比特(20字节)的HMAC-SHA-1运算结果中抽取4字节的动态二进制码。

    DT(String) // String = String[0]...String[19]

    令OffsetBits作为String[19]的低4位

    Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15

    令P = String[OffSet]...String[OffSet+3]

    返回P的后31位

    不取P的最高位的原因是为了防止混淆有符号与无符号的模运算。不同的处理器回进行不同的处理,不取符号位可以消除这种模糊。

    Digit必须最少取6位,可能的话取7到8位码。根据保密需要,应该考虑使Digit = 7或更高以得到长HOTP值。

    5.4 Digit =6的HOTP值计算举例

    下面的例子描述了抽取动态二进制码的过程,hmac_result是存放HMAC-SHA-1结果的一个字节数组

    int offset   =  hmac_result[19] & 0xf ;

            int bin_code = (hmac_result[offset]  & 0x7f) << 24

               | (hmac_result[offset+1] & 0xff) << 16

               | (hmac_result[offset+2] & 0xff) <<  8

               | (hmac_result[offset+3] & 0xff) ;

     

       SHA-1 HMAC Bytes (Example)

       -------------------------------------------------------------

       | Byte Number                                               |

       -------------------------------------------------------------

       |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|

       -------------------------------------------------------------

       | Byte Value                                                |

       -------------------------------------------------------------

       |1f|86|98|69|0e|02|ca|16|61|85|50|ef|7f|19|da|8e|94|5b|55|5a|

       -------------------------------***********----------------++|

    *最后的字节(第19字节)位16进制值0x5a。

    *低4位值是0xa(offset值)

    *offset值是字节10(0xa)

    *从10字节开始4字节值为0x50ef7f19,即动态二进制码DBC1。

    *由于DBC1的最高字节是0x50,所以DBC2=DBC1=0x50ef7f19。

    *HOTP= DBC2 modulo 10^6 = 872921。

    我们将动态二进制码看作31比特的无符号大尾整数,最高字节通过与0x7f进行与运算而屏蔽掉最高位。

    之后我们就可以将这个数与1,000,000 (10^6)模运算产生6位十进制HOTP值872921。

    6.安全性分析

    在附录中详细讨论的安全性分析的结论是:对于所有实用目的,对于不同的计数器输入,动态截短的输出是各不相同且相互独立的31比特数串。

    安全性分析之后详细说明了将一个数串转换为整数最后与10^{digit}进行模运算的影响。这里Digit是HOTP值的位数。

    分析还证明最后的步骤引入了一个可忽略的误差,这并不影响HOTP算法的安全性,从这个意义上说,针对HOTP函数最可能的攻击就是蛮力攻击。

    假设一个敌人可以观察到众多的协议交换并收集了一系列成功的认证值。则这个敌人会试图在他的观察基础上建立函数F来产生HOTP值,但他在随机猜测中并不会有明显的优势。

    合乎逻辑的结论就是最好的策略仍然是蛮力攻击,用枚举发来尝试所有可能的值。

    考虑到本文附录中的安全性分析具有普遍性,我们可以通过下列方程近似模拟HOTP算法的安全性。

    Sec = sv/10^Digit

    这里:

    Sec表示敌人成功的概率

    s是前顾同步窗口大小

    v是认证尝试次数

    Digit是HOTP值的位数。

    很明显,我们可以设置s,T(减少攻击者尝试次数的阈值)和Digit的值,直到获得一定程度的安全性,并仍保持系统的可用性。

    7.安全性要求

    任何一次性口令算法的安全性都是由实现它的应用软件和认证协议所确定,因此,这一节将讨论我们所选择的算法对认证协议和认证软件所提出的临界安全性要求。

    这一节讨论的参数T和s对安全性有重大影响,第6节中的更多细节将在这些参数和他们对系统安全性的影响的关系间详细阐述。

    同样应当注意的是HOTP算法并不能代替加密,也不能提供数据传输的保密性。应当采用其他一些装置来抵抗目的为破坏交易保密性的攻击。

    7.1认证协议要求

    本节我们介绍对实现HOTP在示证者和验证者之间作为认证方法的协议P的一些要求。

    要求1 P必须支持双因子认证,即通信并认证你所知(如口令、通行短语和PIN等一些秘密码)和所有(令牌)。仅有用户知道秘密码,并输入一次性口令值进行认证(双因子认证)。

    要求2P应该不易受蛮力攻击的伤害,这意味着在认证服务器端需要一个阈值或锁定机制。

    要求3P应该在一个安全通道上实现以保护用户隐私和抵抗重放攻击。

    7.2HOTP值的确认

    HOTP客户端(软件或硬件令牌)将其计数器值加1后计算出HOTP客户端的下一个HOTP值。如果HOTP服务器接收到的值客户端计算的值一致,则该HOTP值通过认证。在这种情况下,服务器计数器的值加1.

    如果HOTP服务器接收到的值客户端计算的值不一致,服务器在要求用户提供下一个通行码之前将启动重同步协议(前顾窗口)。

    若重同步失败,服务器会要求协议的下一个认证通行码,直到达到最大认证尝试次数。

    如果达到最大认证尝试次数,服务器应当锁定该帐户并启动一个进程来通知该用户。

    7.3服务器的阈值

    将HMAC-SHA-1值截短成一个较短的数值会增大蛮力攻击的成功率,因此认证服务器需要检测并阻止蛮力攻击。

    我们建议设置一个阈值参数T,其定义为一次性口令认证的最大尝试次数。认证服务器管理每一个HOTP装置所有的单独计数器来记录每一次失败的尝试。我们建议T值不能过大,特别是服务器上使用的重同步方法是基于窗口的,而窗口尺寸较大。T应当越低越好,同时仍能保证可用性不受很大影响。

    另一种可选择的方法是执行一种延迟机制来躲避蛮力攻击。在一次尝试A失败后,认证服务器将等待T*A秒,而这个值是不断增加的。例如令T=5,则一次尝试失败后,服务器等待5秒,两次失败后等待10秒等等。

    延迟或锁定机制都必须是跨登录会话的,以阻止基于多平行猜测技术的攻击。

    7.4计数器的重同步

    尽管服务器计数器的值仅在成功的HOTP认证后增值,令牌计数器的值每当应用户要求产生一个新HOTP值后就会增值。由于这个原因,服务器计数器和令牌计数器就有可能不同步。

    我们建议在服务器上设置一个前顾参数,其定义了前顾窗口的大小。简单地说,服务器计算下面s个HOTP-sever值,并与收到的HOTP客户端值比较。

    在这种情况下计数器同步只需要计数器仅需要服务器计算下一个HOTP值并确定是否相配。有时系统可能要求用户发送一系列HOTP值(如2,3个)来进行重同步,因为伪造一系列连续的HOTP值要比猜测一个HOTP值更困难。

    参数s的上限值保证了服务器不会不停检测HOTP值,同时也限制了试图制造HOTP值的攻击者的可能解空间。s的值应当越小越好,同时保证可用性不受很大影响。

    7.5共享密钥的管理

    对用于产生和认证OTP值的共享密钥进行处理的运算应当安全地运行,以降低敏感信息泄露的风险。本节我们将描述不同的运算和技术方式,以展示对数据安全领域里的不同运作模式。

    现在我们考虑在认证系统中两种不同的产生和存储共享密钥的途径。

    确定性生成:同时在提供和认证阶段都由主种子得出密钥,在需要时都能即时产生。

    随机性生成:在提供阶段密钥随机产生,而且要被及时存储,并在整个有效周期中保持安全。

    确定性生成

    一种可行的方法是从主密钥中得出共享密钥,该主密钥只能存储在服务器中。必须使用一种防篡改的装置来存储该主密钥,并能从该主密钥和公共信息中得出共享密钥。这样做的主要优点是可以在任何时候都不会暴露该共享密钥,避免了对存储的特殊要求,因为共享密钥在提供和认证阶段都可以根据需要随时产生。

    考虑两种不同情况

    使用一个主密钥MK产生共享密钥,每一个HOTP装置都有一个不同的密钥。K_i = SHA-1 (MK,i),这里i表示用以唯一标志一个HOTP装置的公共信息,可以是序列号、一个令牌标识符等等。很明显,存在着一个应用或服务的背景——不同的应用或服务应当提供不同的密钥和设置。

    使用若干主密钥MK_i,每一个HOTP装置存储个从中得到的互不相同的密钥。{K_i,j = SHA-1(MK_i,j)},这里j表示标识装置的公共信息。其思想是只需在认证服务器和硬件安全模块中存储有效主密钥,采用密钥分享法例如[Shamir]将其放置在一个安全的地方。在这种情况下,如果一个主密钥MK_i被泄露,只需要换用另一个而不必替换所有的装置。

    确定性方法的缺点是当主密钥被泄露后,攻击者很容易在正确公共信息的基础上重建任何共享密钥。因此必须废除所有的密钥,或在多主密钥系统中换用另一组密钥。

    在另一方面,用于存储主密钥和产生共享密钥的装置必须可以防止篡改,而且,HSM不能暴露于认证系统的安全范围之外以减少泄漏的风险。

    随机性生成

    随机生成共享密钥。我们建议依据[RFC4086]中的规定并选择好而安全的随机信号发生器来产生密钥。一个随机信号发生器要有随机性的自然发生源。实际中,可以考虑用两种方法来产生共享密钥。

    *基于硬件的发生器:通过物理现象产生随机数。可以利用振荡器来实现并

    并以主动攻击更难以执行的方式构建。

    *基于软件的发生器:设计一个好的软件发生器并非易事。一种简单而又有

    效的方法是使用多种信号源并将单向函数例如SHA-1应用于抽样序列。

    我们推荐使用已经过证明的产品,可以是硬件或软件发生器来计算共享密钥。

    我们同样建议使用安全方法存储密钥。更特别地,可使用防篡改的硬件加密存储器对共享密钥加密,并且仅在需要的时候才将其取出。

    例如,当需要认证一个HOTP值时,共享密钥将被解密,之后将被立即加密以减少其在RAM中暴露的时间。存储共享密钥的数据存储器必须处于一个安全的环境中,以尽量避免对认证系统和保密数据库的攻击。

    特别地,对共享密钥的使用权应当限制在认证系统的程序和进程中。我们不再详细说明所用的各种安全装置。但很明显,对共享密钥的保护是最重要的事。

    8.合成共享密钥

    在共享密钥K中也许会需要附加一些认证因素,组成这些附加的数据可以是由令牌知道而不易被其他人获得的任何数据。这些数据可以包括:

    * 用户输入令牌的PIN或口令

    * 电话号码

    * 任何令牌能以编程方式得到的任何唯一的识别码

    在这种方案下,在提供阶段共享密钥K由一个随机种子值联合一或多个附加认证值复合而成。服务器可以根据需要产生或存储密钥——不论哪种情况都依赖于执行选择。令牌仅存储种子值。当令牌执行HOTP运算,它从种子值和其他局部导出或输入的认证因子值计算出K。

    使用合成共享密钥可以通过引入令牌中的附加认证因子加强基于HOTP的认证系统。

    在某种程度上,令牌是一个可信的设备。而且这种方法可以避免将认证因子(如用户输入PIN)暴露给其他设备。

    9.双向认证

    有趣的是,HOTP服务端也可以用来认证服务器,宣称其为知道共享密钥的真实实体。

    由于HOTP服务器和客户端是同步的并共享同一密钥(或用某种方法重算),可以使用一种简单的3通过协议。

    终端用户输入令牌口令和第一个OTP值OTP1。

    服务器检查OTP1,如果正确,返回OTP2。

    终端用户使用他的HOTP装置检查OTP2,如果正确就进入为网站。

    很明显,正如前文所述,所有的OTP通信都应在安全信道上进行。例如SSL/TLS, Ipsec链路。

    10.结论

    本文描述了HOTP, 基于HMAC的一次性口令生成算法。同时也推荐了较好的执行工具和实现该算法的相关方法。

    本文同时展示了安全因素并证明了HOTP算法是实用且合理的。最可能的攻击即蛮力攻击可以通过认证服务器中计数器的仔细运行而阻止。

    最后,为了改善安全性以满足某些特定应用的需要提出了一些增强方法。

    展开全文
  • multiOTP开源软件已通过OATH认证,适用于HOTP / TOTP (c)2010-2021年SysCo通信系统 当前版本:5.8.2.1(2021-04-08) 二进制下载: : (包括虚拟设备映像) 可用的Docker容器: docker run --mount source = ...
  • 基于时间(TOTP)和基于HMAC(HOTP)的一次性密码库 关于 otplib是一个JavaScript一次性密码(OTP)库,用于生成和验证OTP。 它同时实现了 - 和 - ,并针对各自RFC规范中提供的测试向量进行了测试。 这些数据集...
  • 锈-otp rust-otp是一个Rust库,用于执行和根据。 这些也是许多基于移动设备的2FA应用程序(例如和 ...}" , otp :: make_hotp ( "base32secret3232" . to_ascii_uppercase (). as_str (), 0 ). unwrap ()); assert_eq!
  • TypeScript一次性密码库(HOTP,TOTP,Google身份验证器) 在Node.js环境中工作的一次性密码库 要求 npm install -g tsd gulp 建造 npm install tsd install gulp build 运行单元测试 gulp test 用法 var Google...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 786
精华内容 314
关键字:

hotp