专栏首页Kubernetes手记Python基于License的项目授权机制
原创

Python基于License的项目授权机制

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

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • .NET 基金会项目介绍-IdentityServer

    IdentityServer 是属于 .Net 基金会的一个项目,本文将简要介绍该项目相关的信息。

    newbe36524
  • CAM之基于项目授权子账号管理资源

    背景:公司资源比较多,项目也比较多,怎么让负责个别项目的员工单独管理个别项目里的云资源,可以通过如下方法:

    Z .H
  • python的构建工具setup.py的方法使用示例

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    狼啸风云
  • 常见的开源协议

    参考文章: https://zh.wikipedia.org/wiki/Apache%E8%AE%B8%E5%8F%AF%E8%AF%81 https://...

    续写经典
  • 赞!全球首款开源的堡垒机:JumpServer,附详细部署讲解!

    JumpServer 是全球首款开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 规范的运维安全审计系统。

    网络技术联盟站
  • 实战 | 怎么激活IMC授权?

    安装并启动iMC后,访问主iMC系统登陆界面,点击“产品注册”,进入注册操作选择窗口。

    网络技术联盟站
  • 了解这些,才算真正知道开源!

    没有开源软件,现在的互联网根本无法存在,开源的历史可以追溯到 ARPANET 建立。开源在今天已经不再是一个时髦的词了,对于互联网的开发者来说,它现在就像空气和...

    昱良
  • 不懂开源软件?你需要这篇文章

    没有开源软件,现在的互联网根本无法存在,开源的历史可以追溯到ARPANET建立。开源在今天已经不再是一个时髦的词了,对于互联网的开发者来说,它现在就像空气和水一...

    美码师
  • 对开源的认知

    没有开源软件,现在的互联网根本无法存在,开源的历史可以追溯到ARPANET建立。开源在今天已经不再是一个时髦的词了,对于互联网的开发者来说,它现在就像空气和水一...

    半吊子全栈工匠
  • 【重磅】基于 Python3 的开源堡垒机 Jumpserver v1.0正式发布

    Jumpserver是一款开源堡垒机产品(GPLv2 License),使用 Python3 和 Django1.11 开发。自2014年诞生以来,经历了从 v...

    小小科
  • 百度:人脸登录集成

    为人脸登录提供人脸注册集合,基于人脸进行无动作活体检测、及后台在线活体检测算法,判断用户为真人,保障业务环节中的用户真实性判断。

    WindWant
  • ICG5000G/T/ICG6000系列路由器license首次注册和激活

    需在PC上搭建TFTP Server(推荐用户使用3CDaemon搭建TFTP服务器),注意需要关闭终端的防火墙和无线网卡。

    网络技术联盟站
  • pytest文档56-插件打包上传到 pypi 库

    pytest 的插件完成之后,可以上传到 github,方便其他小伙伴通过 pip 源码安装。如果我们想通过 pip install packages 这种方式...

    上海-悠悠
  • Gowin(高云)FPGA IDE激活

    按道理说是,这个软件的key一天的时间就能下来。但是我一直没有收到,今天我又申请了一遍,睡了一觉起来,邮箱里面就有了。原因是里面我没有在申请的时候吧MAC(计算...

    云深无际
  • 说说开源那些事儿

    前段时间我们遇到了一个情况,课程推送发出来之后有位同学在群里提到,机构提供的项目就是他同学导师的开源项目。

    崔庆才
  • 使用Red-Shadow扫描AWS IAM中的安全漏洞

    Red-Shadow是一款功能强大的AWS IAM漏洞扫描工具,该工具可以帮助你扫描AWS IAM中的错误配置与安全漏洞。

    FB客服
  • 码云小课堂 | 主流的开源协议有哪些?我们该如何选择?

    主流的开源协议有哪些?我们该如何选择? License是软件的授权许可,里面详尽表述了你获得代码后拥有的权利,可以对别人的作品进行何种操作,何种操作又是被禁止的...

    码云Gitee
  • Gitlab的“DevSecOps发展蓝图”概览

    印象中,Gitlab作为Github的竞品,是一款“仓库管理系统”。但,士别三日,当刮目相看。

    用户1713112
  • 安装jumpserver

    Jumpserver是一款使用Python, Django开发的开源跳板机系统,,基于ssh协议来管理,客户端无需安装agent,助力互联网企业高效 用户、资产...

    端碗吹水

扫码关注云+社区

领取腾讯云代金券