代码加密与license控件

代码加密

.pyc也有一定的保护性,容易被反编译出源码…

项目发布时,为防止源码泄露,需要对源码进行一定的保护机制,本文使用Cython将.py文件转为.so进行保护。这一方法,虽仍能被反编译,但难度会比较大。另外,Cython是Python的超集。

创建complie.py文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# !/usr/bin/env python
# -*- coding:utf-8 -*-
'''
Created on 2022/5/31 10:45

@author: syy_ysk

'''
from Cython.Build import cythonize
from Cython.Distutils import build_ext
from setuptools import setup
from setuptools.extension import Extension

setup(
    ext_modules=cythonize(
        [
            Extension('license.aes', ['license/aes.py']),
            Extension('license.lincese_help', ['license/lincese_help.py']),
            Extension('utils.connector', ['utils/connector.py']),
            Extension('utils.CW', ['utils/CW.py']),
            Extension('utils.display_by_person', ['utils/display_by_person.py']),
            Extension('utils.embedder', ['utils/embedder.py']),
            Extension('utils.face_functions', ['utils/face_functions.py']),
            Extension('utils.facenet', ['utils/facenet.py']),
            Extension('utils.sort_images', ['utils/sort_images.py']),
            # 需要保护的.py文件目录写在此处
        ],
        build_dir='build',
        compiler_directives=dict(
            always_allow_keywords=True, language_level=3
        )
    ),
    cmdclass=dict(
        build_ext=build_ext
    )
)

language_level代表python3版本,python2就写2

运行命令:python compile.py build_ext –inplace

删除之前.py文件即可

license控件

撰写aes.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# !/usr/bin/env python
# -*- coding:utf-8 -*-
'''
Created on 2022/2/18 10:31

@author: syy_ysk

'''
'''
1、加一个限制
2、做一个分离:把训练还有调用分开
3、写接口
4、测试
'''
"""
AES加密解密工具类
数据块128位
key 为16位
字符集utf-8
输出为base64
AES加密模式 为cbc
填充 pkcs7padding
"""

import base64
from Crypto.Cipher import AES



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("abc123","abc123")
    return aes_helper
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# !/usr/bin/env python
# -*- coding:utf-8 -*-
'''
Created on 2022/2/18 15:05

@author: syy_ysk

'''
import uuid
import hashlib
import datetime
import subprocess
from license.aes 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('shly_ymcc' + 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):
        # windows 下获取磁盘uuid
        file = subprocess.Popen("wmic csproduct list full | findstr UUID", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 使用管道
        UUID = str(file.stdout.read()[5:-1], encoding='utf-8')
        UUID = UUID.replace('\r', '')# 获取输出结果
        # print(UUID)
        # file = os.system("wmic csproduct list full | findstr UUID")
        # UUID = file[5:-1].replace('\n', '').replace('\r', '')
        
        # linux 下获取磁盘uuid
        # file = subprocess.Popen("blkid", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 使用管道
        # UUID = str(file.stdout.read(), encoding='utf-8')
        # UUID = UUID.split("\"")[1] # 获取输出结果

        return UUID
        # 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('shly_ymcc' + str(mac_addr))
        if psw == hashed_msg:
            return True
        else:
            return False


oper = LicenseHelper()
def verify(license):
    try:
        license_dic = oper.read_license(license)
        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:
                result = "成功"
            else:
                result = "激活码过期"
        else:
            result = "MAC不匹配, License无效, 请更换License"
    except:
        result = "读取失败, 无效的License"
    return result




if __name__ == '__main__':
    import os
    license_lic = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    oper = LicenseHelper()
    mac = oper.get_mac_address()
    #mac = 'fff8524C-2F81-11B2-A85C-93AC6936373A'
    s = oper.generate_license('2023-02-26 16:30:00', mac)
    # print(s)
    # print(os.path.join(license_lic, "license.lic"))
    with open(os.path.join(license_lic, "license.lic"), "w+") as licFile:
        licFile.write(oper.generate_license('2093-03-26 15:50:00', mac))
        licFile.close()
        print("ok")
    print(verify(s))
    s = oper.check_license_psw('60fea10e710aebfabc2c52f9e6e9ca19994c3d269260e9782406def9c573f76f')
    print(s)


代码加密