取磁盘编码或者网卡编码+时间戳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位中的部分,降低被暴力破解的可能。