0
点赞
收藏
分享

微信扫一扫

使用Google Authenticator实现openvpn二次验证--Google Authenticator的python实现

OPENVPN的验证方式很灵活,可以通过ldap,mysql,也可以进一步扩展,实现使用Google Authenticator实现二次验证的方式来实现。

Google Authenticator

其原理并不复杂:

客户端和服务器事先协商好一个密钥K,用于一次性密码的生成过程,此密钥不被任何第三方所知道。此外,客户端和服务器各有一个计数器C,并且事先将计数值同步。

进行验证时,客户端对密钥和计数器的组合(K,C)使用HMAC(Hash-based Message Authentication Code)算法计算一次性密码,公式如下:

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

上面采用了HMAC-SHA-1,当然也可以使用HMAC-MD5等。HMAC算法得出的值位数比较多,不方便用户输入,因此需要截断(Truncate)成为一组不太长十进制数(例如6位)。计算完成之后客户端计数器C计数值加1。用户将这一组十进制数输入并且提交之后,服务器端同样的计算,并且与用户提交的数值比较,如果相同,则验证通过,服务器端将计数值C增加1。如果不相同,则验证失败。



客户端和服务器事先协商好一个密钥K,用于一次性密码的生成过程,此密钥不被任何第三方所知道。此外,客户端和服务器各有一个计数器C,并且事先将计数值同步。

进行验证时,客户端对密钥和计数器的组合(K,C)使用HMAC(Hash-based Message Authentication Code)算法计算一次性密码,公式如下:

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

上面采用了HMAC-SHA-1,当然也可以使用HMAC-MD5等。HMAC算法得出的值位数比较多,不方便用户输入,因此需要截断(Truncate)成为一组不太长十进制数(例如6位)。计算完成之后客户端计数器C计数值加1。用户将这一组十进制数输入并且提交之后,服务器端同样的计算,并且与用户提交的数值比较,如果相同,则验证通过,服务器端将计数值C增加1。如果不相同,则验证失败。(链接:https://www.zhihu.com/question/20462696/answer/19670601)

示例代码

使用python实现的代码如下,这段代码还是实现了验证,生成,生成二维码,生成二维码图像base64编码等功能。

AuthCode.py

import base64
import hashlib
import hmac
import time
import datetime
import qrcode
import random as _random
from PIL import Image
import io

'''

'''


class AuthCode:

    def byte_secret(self, secret):
        # print(self)
        missing_padding = len(secret) % 8
        if missing_padding != 0:
            secret += '=' * (8 - missing_padding)
        return base64.b32decode(secret, casefold=True)

    def int_to_bytestring(self, i, padding=8):
        # print(self)
        result = bytearray()
        while i != 0:
            result.append(i & 0xFF)
            i >>= 8
        return bytes(bytearray(reversed(result)).rjust(padding, b'\0'))

    # 根据约定的密钥计算当前动态密码
    def generate_otp(self, secret, timestamp=None):
        if not timestamp:
            timestamp = datetime.datetime.now()
        for_time = timestamp
        i = time.mktime(for_time.timetuple())
        intput = int(i / 30)
        digest = hashlib.sha1
        digits = 6
        if intput < 0:
            raise ValueError('input must be positive integer')
        hasher = hmac.new(self.byte_secret(secret), self.int_to_bytestring(intput), digest)
        hmac_hash = bytearray(hasher.digest())
        offset = hmac_hash[-1] & 0xf
        code = ((hmac_hash[offset] & 0x7f) << 24 |
                (hmac_hash[offset + 1] & 0xff) << 16 |
                (hmac_hash[offset + 2] & 0xff) << 8 |
                (hmac_hash[offset + 3] & 0xff))
        str_code = str(code % 10 ** digits)
        while len(str_code) < digits:
            str_code = '0' + str_code
        return str_code

    # 随机生成一个base32密钥
    def random_base32(self, length=32, random=_random.SystemRandom(),
                      chars=None):
        # print(self)
        if chars is None:
            chars = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
        return ''.join(
            random.choice(chars)
            for _ in range(length)
        )

    # 生成二维码地址
    def getQRCodeGoogleUrl(self, name, secret):
        """
        :param self:
        :param name:  用户名
        :param secret: 秘钥
        :return:
        """
        # print(self)
        return "otpauth://totp/" + name + "?secret=%s" % secret

    # 生成二维码base64格式
    def genQr64(self, url):
        # print(self)
        qr = qrcode.make(url)
        buf = io.BytesIO()
        qr.save(buf)
        img_buf = buf.getvalue()
        img_stream = base64.b64encode(img_buf)
        return img_stream

    def save_base64_qr_to_image(self, base64_str, filename="qrcode.png"):
        # print(self)
        # 将 base64 字符串解码为二进制数据
        image_data = base64.b64decode(base64_str)
        # 使用 io.BytesIO 将二进制数据转换为图片对象
        image = Image.open(io.BytesIO(image_data))
        # 保存图片
        image.save(filename)
        return filename

    def get_code(self, sKey):
        """
        使用说明
        使用random_base32 生成 密钥 sKey
        使用generate_otp(sKey) 生成 code 时间戳默认为当前,一般情况要传递30s 60s 90s 120s 之前 共计五个时间戳来生成code
        """
        # print(self)
        now = datetime.datetime.now()
        now_30s = now - datetime.timedelta(seconds=30)
        now_60s = now - datetime.timedelta(seconds=60)
        now_90s = now - datetime.timedelta(seconds=90)
        now_120s = now - datetime.timedelta(seconds=120)
        code_list = [self.generate_otp(sKey, now),
                     self.generate_otp(sKey, now_30s),
                     self.generate_otp(sKey, now_60s),
                     self.generate_otp(sKey, now_90s),
                     self.generate_otp(sKey, now_120s)]
        return code_list

    def verify_code(self, two_code, sKey):
        """
        验证二次登录验证码
        """
        if two_code in self.get_code(sKey):
            return True
        else:
            return False


if __name__ == "__main__":
    # # 获取秘钥
    # Key = AuthCode().random_base32()
    # print("google_auth:" + Key)
    #
    # # 获取二维码
    # u = AuthCode().getQRCodeGoogleUrl("glen", Key)
    # #
    # base64str = AuthCode().genQr64(u)
    # # print(AuthCode().save_base64_qr_to_image(base64str))
    # # 获取code阵列
    # print(AuthCode().get_code(Key))
    # # 验证方法
    code = '468916'
    key = 'IJ725J46SAJ6J2PJZ2C4R6Y5MD3MBI7R'
    code_list = AuthCode().get_code(key)
    print(code_list)
    if code in code_list:
        print("ok")
    else:
        print('fail')

使用Google Authenticator实现openvpn二次验证--Google Authenticator的python实现_Google Authenticator

举报

相关推荐

0 条评论