【python】程序绑定PC和时间戳做定期授权码

取磁盘编码或者网卡编码+时间戳16位格式

关于时间戳,当前简单的实现是年月日转成对应的16位,比如 23-03-28  --> 173E   (日先直接整除2,每月不会超过31天,整除后不会超过16;或者用32进制标识)

另一种实现方式是详细计算今天与00-01-01相隔的天数,100年最多36500天, 4位0xFFFF包含了65535个数字,是能用4位的16位字符串表示100年的。

# auth.py
# -*- coding: utf-8 -*-

import wmi
import time
from hashlib import md5

class Auth:
    def __init__(self):
        self.today = self.get_hex_day()
        self.sn = self.get_machine_code()

    def get_hex_day(self):
        today = time.strftime("%y-%m-%d", time.localtime())
        year, month, day = today.split("-")
        year = hex(int(year))[2:]
        if len(year) == 1:
            year = "0{}".format(year)
        month = hex(int(month))[2:]
        day = hex(int(day)//2)[2:]
        number = "{}{}{}".format(year, month, day)
        return number

    def get_machine_code(self):
        machine = wmi.WMI()
        code = self.get_disk_code(machine)
        if not code:    # 解决虚拟机的验证
            code = self.get_network_mac_code(machine)
        return code

    def get_disk_code(self, machine):
        disk_list = machine.Win32_PhysicalMedia()
        if not disk_list:
            return
        sn = disk_list[0].SerialNumber
        if not sn:
            return
        sn = sn.replace("_", "").replace("-", "")
        return "{}-{}-{}".format(sn[0:4], sn[4:8], sn[-5:-1])

    def get_network_mac_code(self, machine):
        for nw in machine.Win32_NetworkAdapterConfiguration():
            if nw.MacAddress:
                mac = nw.MacAddress.replace(":", "")
                return "{}-{}-{}".format(mac[0:4], mac[4:8], mac[-5:-1])
        return

    def check_auth_code(self, sn, code):
        sand = "$sandvalue${}".format(sn).encode("utf-8")
        value = md5(sand).hexdigest()[8:-8].upper()
        check_code = "-".join([value[x:x+4] for x in range(0, 16, 4)])
        if check_code != code:
            return False
        today = self.sample_caculate_day(time.strftime("%y-%m-%d", time.localtime()))
        year = int(sn[-4:-2], 16)
        month = int(sn[-2:-1], 16)
        day = int(sn[-1], 16) * 2
        init_day = self.sample_caculate_day("{}-{}-{}".format(year, month, day))
        if today - init_day > 90:
            return False
        return True

    def sample_caculate_day(self, string):
        year, month, day = string.split("-")
        return 365*int(year) + 30*int(month) + int(day)

    def get_serial_code(self, timestamp):
        return "{}-{}".format(self.sn, timestamp).upper()

if __name__ == '__main__':
    test = Auth()
    sn = test.get_serial_code(test.today)
    print(sn)
    code = "E09E-8D4E-CA76-BBA7".upper()
    # print(auth.check_auth_code(sn, code))

# auth_diag.py
# coding: utf-8
from auth import Auth

import wx


class DataXferValidator(wx.PyValidator):  # 声明验证器
    def __init__(self, data, key):
        wx.PyValidator.__init__(self)
        self.data = data
        self.key = key

    def Clone(self):
        """
Note that every validator must implement the Clone() method.
"""
        return DataXferValidator(self.data, self.key)

    def Validate(self, win):  # 没有验证数据
        return True

    def TransferToWindow(self):  # 对话框打开时被调用
        textCtrl = self.GetWindow()
        textCtrl.SetValue(self.data.get(self.key, ""))
        return True

    def TransferFromWindow(self):  # 对话框关闭时被调用
        textCtrl = self.GetWindow()
        self.data[self.key] = textCtrl.GetValue()
        return True


class AuthDialog(wx.Dialog):
    def __init__(self, data):
        about_txt = """当前工具未授权或授权已超期\n请将SN发送给xxx,获取对应授权码."""
        wx.Dialog.__init__(self, None, -1, "验证")

        # Create the text controls
        about = wx.StaticText(self, -1, about_txt)
        sn = wx.StaticText(self, -1, "SN:")
        code = wx.StaticText(self, -1, "Code:")

        # 将验证器与窗口部件相关联
        sn_input = wx.TextCtrl(self, validator=DataXferValidator(data, "sn"))
        code_input = wx.TextCtrl(self, validator=DataXferValidator(data, "code"))

        # Use standard button IDs
        okay = wx.Button(self, wx.ID_OK)
        okay.SetDefault()
        cancel = wx.Button(self, wx.ID_CANCEL)

        # Layout with sizers
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(about, 0, wx.ALL, 5)
        sizer.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 5)

        fgs = wx.FlexGridSizer(3, 2, 5, 5)
        fgs.Add(sn, 0, wx.ALIGN_RIGHT)
        fgs.Add(sn_input, 0, wx.EXPAND)
        fgs.Add(code, 0, wx.ALIGN_RIGHT)
        fgs.Add(code_input, 0, wx.EXPAND)
        fgs.AddGrowableCol(1)
        sizer.Add(fgs, 0, wx.EXPAND | wx.ALL, 5)

        btns = wx.StdDialogButtonSizer()
        btns.AddButton(okay)
        btns.AddButton(cancel)
        btns.Realize()
        sizer.Add(btns, 0, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(sizer)
        sizer.Fit(self)


class AuthGUI:
    def __init__(self, timestamp=None, code=None):
        self.device = Auth()
        self.auth_pass = False
        self.timestamp = timestamp
        self.code = code
        self.dlg = None
        if timestamp:
            self.sn = self.device.sn + "-" + timestamp
        else:
            self.sn = self.device.sn + "-" + self.device.today
        if code and self.device.check_auth_code(self.sn, code):
            self.auth_pass = True
        # print(self.auth_pass)
        if not self.auth_pass:
            self.timestamp = self.device.today
            self.sn = self.device.sn + "-" + self.device.today
            self.show_diaglog()

    def show_diaglog(self):
        auth_app = wx.App(False)  #  如果被其他GUI调用,需要注释掉此行
        data = {"sn": self.sn, "code": ""}
        self.dlg = AuthDialog(data)
        self.dlg.ShowModal()
        self.dlg.Destroy()
        # print(data)
        if self.device.check_auth_code(self.sn, data.get("code")):
            wx.MessageBox("验证通过")
            self.code = data.get('code')
            self.auth_pass = True
        else:
            wx.MessageBox("验证失败")
        auth_app.MainLoop()  #  如果被其他GUI调用,需要注释掉此行

    def close_diaglog(self):
        if self.dlg:
            self.dlg.Destroy()


if __name__ == "__main__":
    gui = AuthGUI("1FFFF", "23333")
    print(gui.auth_pass)

程序只需要保存当前初始时的时间戳即可,超期时刷新时间戳,程序每次启动时获取PC信息和保存的时间戳,与授权码对比,通过后,再对比时间戳。

解决一号多用的问题,或者拷贝配置识别问题,或者人为修改配置时间戳问题。

注意没有授权码时,这个时间戳记得更新,免得使用了较早时间来申请授权码。

md5校验时加入盐值,并截取32位中的部分,降低被暴力破解的可能。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值