Python 基於 License 的項目受權機制 | Python 主題月

本文正在參加「Python主題月」,詳情查看 活動連接node

1 需求說明

當項目平臺被首次部署在服務器上時,系統是沒有被受權的。python

當但願將平臺部署到某一臺特定的服務器進行使用時,須要提供該服務器的 MAC地址,以及受權到期時間,請求獲取受權碼,收到受權碼後,就能正常使用遷移平臺。linux

受權方收到受權請求時,得到平臺安裝的目標服務器的 MAC地址。經過一套綁定 MAC地址 的算法,生成了一個 License,而且具備 License 失效的時間。生成的 License 同軟件中內置的同一套算法生成的信息進行比對,若是比對上,那麼受權成功。若是比對不上或者受權過時,那麼受權失敗。c++

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的設計以沒法解爲目的;簡單說來就是正向簡單,逆向困難。shell

# 一、獲得密鑰,經過hash算法計算目標計算機的mac地址
 	psw = self.hash_msg('smartant' + str(mac_addr))
 # 二、新建一個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加密的類django

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
複製代碼

查看使用說明bash

獲取MAC地址服務器

生成License

相關文章
相關標籤/搜索