1 需求说明
当项目平台被首次部署在服务器上时,系统是没有被授权的。当客户希望将平台部署到某一台特定的服务器进行使用时,需要提供该服务器的 MAC地址,以及授权到期时间,请求获取授权码,收到授权码后,就能正常使用迁移平台。
授权方收到授权请求时,获得平台安装的目标服务器的 MAC地址。通过一套绑定 MAC地址 的算法,生成了一个 License,并且具有 License 失效的时间。生成的 License 同软件中内置的同一套算法生成的信息进行比对,如果比对上,那么授权成功。如果比对不上或者授权过期,那么授权失败。
2 授权机制流程
2.1 生成授权流程
2.2 验证授权流程
3 代码实现
3.1 获取Mac地址
def get_mac_address(self):
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
3.2 加密Mac地址
算法的核心就是对mac地址进行hash计算。为了增加生成的license文件的困难度,在mac地址之前再加上一个特定的字符,让该license生成软件的破解难度可以稍微提高。例如在这里的示例代码中,特定字符暂定为smartant
。
Hash算法的特点是,HASH的设计以无法解为目的;简单说来就是正向简单,逆向困难。
# 1、得到密钥,通过hash算法计算目标计算机的mac地址
psw = self.hash_msg('smartant' + str(mac_addr))
# 2、新建一个license_str 的字典,用于保存真实的mac地址,license失效时间,加密后的字符串
license_str = {}
license_str['mac'] = mac_addr
license_str['time_str'] = end_date
license_str['psw'] = psw
生成的lincense_str作为一个字典,不能输出作为License,因为可以很直接的看到其组成元素和结果 因此为了更进一步加密,保证生成的License信息是无序且无意义地字符串,采用AEScoder进行加密,这里封装了一个AES加密的类
3.3 AES加密
"""
AES加密解密工具类
数据块128位
key 为16位
字符集utf-8
输出为base64
AES加密模式 为cbc
填充 pkcs7padding
"""
import base64
from Crypto.Cipher import AES
from django.conf import settings
class AESHelper(object):
def __init__(self, password, iv):
self.password = bytes(password, encoding='utf-8')
self.iv = bytes(iv, encoding='utf-8')
def pkcs7padding(self, text):
"""
明文使用PKCS7填充
最终调用AES加密方法时,传入的是一个byte数组,要求是16的整数倍,因此需要对明文进行处理
:param text: 待加密内容(明文)
:return:
"""
bs = AES.block_size # 16
length = len(text)
bytes_length = len(bytes(text, encoding='utf-8'))
# tips:utf-8编码时,英文占1个byte,而中文占3个byte
padding_size = length if(bytes_length == length) else bytes_length
padding = bs - padding_size % bs
# tips:chr(padding)看与其它语言的约定,有的会使用'\0'
padding_text = chr(padding) * padding
return text + padding_text
def pkcs7unpadding(self, text):
"""
处理使用PKCS7填充过的数据
:param text: 解密后的字符串
:return:
"""
length = len(text)
unpadding = ord(text[length-1])
return text[0:length-unpadding]
def encrypt(self, content):
"""
AES加密
模式cbc
填充pkcs7
:param key: 密钥
:param content: 加密内容
:return:
"""
cipher = AES.new(self.password, AES.MODE_CBC, self.iv)
content_padding = self.pkcs7padding(content)
encrypt_bytes = cipher.encrypt(bytes(content_padding, encoding='utf-8'))
result = str(base64.b64encode(encrypt_bytes), encoding='utf-8')
return result
def decrypt(self, content):
"""
AES解密
模式cbc
去填充pkcs7
:param key:
:param content:
:return:
"""
cipher = AES.new(self.password, AES.MODE_CBC, self.iv)
encrypt_bytes = base64.b64decode(content)
decrypt_bytes = cipher.decrypt(encrypt_bytes)
result = str(decrypt_bytes, encoding='utf-8')
result = self.pkcs7unpadding(result)
return result
def get_aes():
# AES_SECRET和AES_IV分别为密钥和偏移量
aes_helper = AESHelper(settings.AES_SECRET, settings.AES_IV)
return aes_helper
生成License代码
def generate_license(self, end_date, mac_addr):
print("Received end_date: {}, mac_addr: {}".format(end_date, mac_addr))
psw = self.hash_msg('smartant' + str(mac_addr))
license_str = {}
license_str['mac'] = mac_addr
license_str['time_str'] = end_date
license_str['psw'] = psw
s = str(license_str)
licence_result = get_aes().encrypt(s)
return licence_result
3.4 最终代码
import uuid
import hashlib
import datetime
from common.aes_encrypt import get_aes
class LicenseHelper(object):
def generate_license(self, end_date, mac_addr):
print("Received end_date: {}, mac_addr: {}".format(end_date, mac_addr))
psw = self.hash_msg('smartant' + str(mac_addr))
license_str = {}
license_str['mac'] = mac_addr
license_str['time_str'] = end_date
license_str['psw'] = psw
s = str(license_str)
licence_result = get_aes().encrypt(s)
return licence_result
def get_mac_address(self):
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
def hash_msg(self, msg):
sha256 = hashlib.sha256()
sha256.update(msg.encode('utf-8'))
res = sha256.hexdigest()
return res
def read_license(self, license_result):
lic_msg = bytes(license_result, encoding="utf8")
license_str = get_aes().decrypt(lic_msg)
license_dic = eval(license_str)
return license_dic
def check_license_date(self, lic_date):
current_time = datetime.datetime.strftime(datetime.datetime.now() ,"%Y-%m-%d %H:%M:%S")
current_time_array = datetime.datetime.strptime(current_time,"%Y-%m-%d %H:%M:%S")
lic_date_array = datetime.datetime.strptime(lic_date, "%Y-%m-%d %H:%M:%S")
remain_days = lic_date_array - current_time_array
remain_days = remain_days.days
if remain_days < 0 or remain_days == 0:
return False
else:
return True
def check_license_psw(self, psw):
mac_addr = self.get_mac_address()
hashed_msg = self.hash_msg('smartant' + str(mac_addr))
if psw == hashed_msg:
return True
else:
return False
oper = LicenseHelper()
read_bool, license_dic = oper.read_license(license)
if not read_bool:
res['status'] = False
res['msg'] = "读取失败, 无效的License, 错误信息: {}".format(license_dic)
return Response(res, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
date_bool = oper.check_license_date(license_dic['time_str'])
psw_bool = oper.check_license_psw(license_dic['psw'])
if psw_bool:
if date_bool:
res['status'] = True
res['time'] = license_dic['time_str']
res['msg'] = ""
else:
res['status'] = False
res['time'] = license_dic['time_str']
res['msg'] = "激活码过期"
else:
res['status'] = False
res['time'] = license_dic['time_str']
res['msg'] = "MAC不匹配, License无效, 请更换License"
if psw_bool and date_bool:
serializer_content = {
"license": license,
"end_time": license_dic['time_str']
}
license_serializer.save(**serializer_content)
return Response(res, status=status.HTTP_200_OK)
else:
return Response(res, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
4 运行结果
4.1 正常激活
4.2 已到期
4.3 MAC不正确(不在授权的机器上运行代码)
5 Shell脚本
#!/bin/bash
# Defining variables
create_license="./libs/create.py"
show_mac="./libs/showmac.py"
function echo_green(){
echo -e "\033[32m$1\033[0m $2"
}
function echo_red(){
echo -e "\033[31m$1\033[0m $2"
}
function echo_yellow(){
echo -e "\033[33m$1\033[0m $2"
}
function echo_blue(){
echo -e "\033[34m$1\033[0m $2"
}
# Step1 Check Python Environoment
function env_check(){
clear
echo_blue "[Step1]" "Python env check dependencies packages...plz wait."
pip3 list --format=columns | grep pycrypto &>/dev/null
python_env=$?
if [[ ${python_env} -eq 0 ]];then
echo_green "[Step1]" "Done"
else
yum install -y gcc gcc-c++ python36 python36-pip python36-devel && clear
pip3 install pycrypto -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com && clear
if [[ $? -eq 0 ]];then
echo_blue "[Step1]" "Python env check dependencies packages...plz wait."
echo_green "[Step1]" "Done"
else
echo_red "[Error]" "Python config error" && exit 1
fi
fi
}
# Step2 Input EndTime and MAC, Create License
function generate_license(){
while true
do
echo_blue "[Step2] Please enter the expiration time of the license: (eg: 2021-04-05 12:00:00)" && read end_time
if [ -n "${end_time}" ];then
if date +"%d-%b-%y %H:%M:%S" -d "${end_time}" >> /dev/null 2>&1; then
echo_green "[Step2]" "Date Provided by user : ${end_time}"
break
else
echo_red "[Error]" "Wrong date format please input correct format like: 2021-04-05 12:00:00"
fi
fi
done
while true
do
echo_blue "[Step2] Please enter the MAC address of the server: (eg: 52:54:f5:a7:dc:4c)" && read mac
if [ -n "${mac}" ];then
break
fi
done
echo_yellow "[Step2] The expiraion time is: ${end_time}, MAC is: ${mac}"
if [ -n "${end_time}" ] && [ -n "${mac}" ];then
license=`python3 ${create_license} -t "${end_time}" -m "${mac}"`
echo_blue "[Finished] Generate License Success:"
echo_green ${license}
else
echo_red "[Error] Create license failed."
exit 1
fi
}
# Show mac address
function show_mac(){
mac_address=`python ${show_mac}`
echo_yellow ${mac_address}
}
# Show usage
function show_usage(){
echo "Usage:"
echo " $0 [command]"
echo "Available Commands:"
echo " -c|create Create a license for smartant platform."
echo " -s|showmac Show mac address for linux server."
}
# Function main
if [ $# -eq 1 ];then
case $1 in
-c|create)
env_check
generate_license
;;
-s|showmac)
show_mac
;;
*)
show_usage
exit 1
esac
else
show_usage
exit 1
fi
查看使用说明
获取MAC地址
生成License
标签:License,license,Python,self,echo,mac,str,time,授权 From: https://www.cnblogs.com/lylsr/p/17701047.html