python项目加密(模型加密,文件加密),涵盖了多种方法以及代码实现

Python作为动态语言一般是以源码方式进行部署的,这就意味着他人在部署机器上可以直接获取项目代码,可能给作者带来不必要的损失和风险,这就需要对代码进行加密或混淆。常规的几类加密(混淆)方式如下:

  1. 编译为pyc文件
  2. 将项目代码打包成python包
  3. 将python文件变异成二进制文件

总体来说1和2的方式还是存在一定的风险,本文将采用第三种方式,且提供五种该类型的加密方式,分别为、Cython、cryptography、RSA、pyAesCrypt和encryptpy。

一、Cython

该方法是通过Cython将python文件编译成.c(C语言)文件,再生成.so(share object)文件,这种方式是不可逆的,所以能够起到混淆代码,起到一定程度的加密效果。
通过命令查看目录结构

$ tree -a .

目录结构如下:

.
├── setup.py
└── example
    ├── __init__.py
    ├── main.py
    ├── README.md
    ├── xxx.py
    └── utils.py

1 directory, 6 files

运行命令

python setup.py build_ext

setup.py文件如下,这段代码可通用,直接运行即可自动生成

# -*- coding: utf-8 -*- 
import os
import shutil
import fnmatch
from distutils.core import setup
from Cython.Build import cythonize


def get_delete_files(project_root):
    """这个函数会遍历指定项目根目录下的所有文件,并返回一个包含所有Python文件的列表。 这个函数会排除名为'main.py'的文件,因为它是主程序文件。"""
    matches = []
    for root, dirnames, filenames in os.walk(project_root):
        for filename in fnmatch.filter(filenames, '*.py'):
            if filename != 'main.py':  # 排除main.py文件
                matches.append(os.path.join(root, filename))
        for filename in fnmatch.filter(filenames, '*.c'):
            matches.append(os.path.join(root, filename))

    return matches


def get_py_files(project_root):
    """这个函数会遍历指定项目根目录下的所有文件,并返回一个包含所有Python文件的列表。 这个函数会排除名为'main.py'的文件,因为它是主程序文件。"""
    matches = []
    for root, dirnames, filenames in os.walk(project_root):
        for filename in fnmatch.filter(filenames, '*.py'):
            if filename != 'main.py':  # 排除main.py文件
                matches.append(os.path.join(root, filename))

    return matches


def tool(root, paths):
    """
    该函数是执行整个任务的主要函数。它以项目的根目录和Python文件的路径列表作为输入。
    :param root:
    :param paths:
    :return:
    """
    filePath3 = os.path.dirname(dis_root) + '/build/'
    if not os.path.exists(filePath3):
        os.mkdir(filePath3)

    # 1、文件加密
    setup(name='encrypt', ext_modules=cythonize(paths))
    print("加密完成")

    # 2、将加密的文件移至对应目录下
    files_1 = os.listdir(filePath3)
    for files_1_temp in files_1:
        if "lib" in files_1_temp:
            files_1 = files_1_temp
            # print(files_1)
    print('filePath3: ', filePath3)
    print('files_1: ', files_1)
    for files_2 in os.listdir(filePath3 + files_1):
        so_file = filePath3 + files_1+"/" + files_2
        print(so_file)

        # 文件移动或拷贝
        shutil.move(so_file, root)

    # 3、删除原文件和生成的附属文件夹
    files2 = get_delete_files(dis_root)
    for file in files2:
        if os.path.exists(file):
            os.remove(file)
            print('移除后test 目录下有文件:%s' % file)
        else:
            print("要删除的文件不存在!")

    # 删除附属文件夹
    try:
        shutil.rmtree(filePath3)
    except Exception as ex:
        print("错误信息:"+str(ex)) # 提示:错误信息,目录不是空的
    print("删除完成")


dis_root = '/mnt/xxx/example'
Paths = get_py_files(dis_root)
tool(dis_root, Paths)

二、加密库 cryptography

环境安装

	pip install cryptography

下面以加密一个onnx文件为例。
先进行加密,可根据配置密钥个数,保障加密强度。然后对文件内容进行加密,当然这个密钥后面在对文件进行解密的时候会派上用场,因此密钥一定要保存完好,代码如下:

import os
import base64
import argparse
from cryptography.fernet import Fernet, MultiFernet


def Parser():
    parser = argparse.ArgumentParser(description='文件加密')
    parser.add_argument('--model_file', '-mf', type=str, default=None, help='需要加密文件的绝对地路径')
    parser.add_argument('--new_model_file', '-nmf', type=str, default=None, help='文件保存的绝对地路径')
    parser.add_argument('--key_num', '-kn', type=int, default=8, help='密钥数量')
    return parser.parse_args()


def generate_key(message='') -> bytes:
    x = os.urandom(32 - len(message)) + message.encode()
    return base64.urlsafe_b64encode(x)


def add_secret(model_file=None, new_model_file=None, multi_key: int = 1):
    if new_model_file is None:
        ext = os.path.splitext(model_file)[-1]
        new_model_file = model_file.replace(ext, '.dll')

    # # 生成密钥
    ks = []
    keys = []
    if multi_key > 0:
        for i in range(multi_key):
            keyi = generate_key()
            keys.append(keyi)
            print(f'key{i + 1}: ', keyi)
            ki = Fernet(keyi)
            ks.append(ki)
        if len(ks) == 1:
            f = ks[0]
        else:
            f = MultiFernet(ks)
    else:
        raise ValueError('The "multi_key" must be greater than 0.')

    # 保存license
    file = 'license.dll'
    with open(file, 'wb') as fw:
        for j in keys:
            fw.write(j + b'\n')
    print('liscence file write in ', file)

    with open(new_model_file, 'wb') as ew:
        # 二进制读取模型文件
        content = open(model_file, 'rb').read()
        # 根据密钥解密文件
        encrypted_content = f.encrypt(content)
        # print(encrypted_content)
        # 保存到新文件
        ew.write(encrypted_content)


def desecrect(new_model_file: str = None, keys_file: str = None):
    """
    :param new_model_file:
    :param keys:
    :return:
    """

    with open(keys_file, 'rb') as f:
        x = f.read()
        keys = x.strip(b'\n').split(b'\n')

    print(keys)
    ks = []
    for keyi in keys:
        ki = Fernet(keyi)
        ks.append(ki)
    if len(ks) > 1:
        f = MultiFernet(ks)
    else:
        f = ks[0]

    onnx_file = open(new_model_file, 'rb').read()
    onnx_file = f.decrypt(onnx_file)
    if onnx_file is not None:
        print('解密成功')

    with open(desecrect_model_file, 'wb') as ew:
        ew.write(onnx_file)


def test():
    dll_file = 'xxx.dll'
    key1 = 'license.dll'
    desecrect(dll_file, key1)


def main():
    args = Parser()
    if args.model_file is None:
        model_file = 'xxx.onnx'
    else:
        model_file = args.model_file

    message = args.message if args.message is not None else ''
    add_secret(message, model_file, args.new_model_file, args.key_num)


if __name__ == '__main__':
    main()
    # test()

三、加密库 RSA

使用RSA加密算法实现数据的加密解密
环境安装

	pip install rsa
import os
import rsa

def encrypt_file(file_path, public_key_file):
    """使用RSA算法加密文件
    
    参数:
    file_path: 需要加密的文件路径
    public_key_file: 公钥文件路径
    
    返回值:
    无
    """
    # 读取文件内容
    with open(file_path, "rb") as file:
        file_content = file.read()
    # 读取公钥
    with open(public_key_file, "rb") as key_file:
        public_key = rsa.PublicKey.load_pkcs1(key_file.read())
    # 加密文件内容
    encrypted_content = rsa.encrypt(file_content, public_key)
    # 将加密后的内容写入文件
    with open(file_path, "wb") as file:
        file.write(encrypted_content)

def decrypt_file(file_path, private_key_file, password):
    """使用RSA算法解密文件
    
    参数:
    file_path: 需要解密的文件路径
    private_key_file: 私钥文件路径
    password: 私钥文件密码
    
    返回值:
    无
    """
    # 读取文件内容
    with open(file_path, "rb") as file:
        encrypted_content = file.read()
    # 读取私钥
    with open(private_key_file, "rb") as key_file:
        private_key = rsa.PrivateKey.load_pkcs1(key_file.read(), password)
    # 解密文件内容
    file_content = rsa.decrypt(encrypted_content, private_key)
    # 将解密后的内容写入文件
    with open(file_path, "wb") as file:
        file.write(file_content)

四、加密库 pyAesCrypt

个人觉得这个代码库实现最为简单,只是密钥需要我们自己指定的
环境安装

	pip install pyAesCrypt
import pyAesCrypt

def Encryption(input_file_path, output_file_path, key):
    pyAesCrypt.encryptFile(input_file_path, output_file_path, key)
    print("File has been decrypted")

def Decryption(input_file_path, output_file_path, key):
    pyAesCrypt.decryptFile(input_file_path, output_file_path, key)
    print("File has been decrypted")

五、加密库 encryptpy

encryptpy使用Cython将Python代码编译为二进制以达到加密的目的,并且支持通过git-diff来获取两次提交间的差异文件,方便地进行编译
环境安装

	pip install encryptpy

Basic Usage

Usage: encryptpy [OPTIONS] COMMAND [ARGS]...

  Encrypt your Python code

Options:
  --config TEXT  The config file, ignore if given is invalid  [default:
                 .encryptpy.cfg]
  --help         Show this message and exit.

Commands:
  clean     Simply clean `build` and `__pycache__` directory in DIRS
  git-diff  Compile files between two COMMITS, see `git-diff`: `--name-only`
  init      Copy src to build-dir and do compile, usually used for the...
  run       Compile given Python code files

子命令用法可以使用encryptpy --help来查看。

例子

例如,有一个名为package_a的项目文件目录:

$ tree -a .
.
├── .encryptpy.cfg
└── package_a
    ├── __init__.py
    ├── main.py
    ├── README.md
    ├── setup.py
    └── utils.py

1 directory, 6 files

其中 .encryptpy.cfg内容如下:

[encryptpy]
; Files will be compiled
paths =
    package_a
; Files will be ignored when compiling, support Regex
ignores =
    setup.py
; For command `init`, files will be ignored when copying, Glob-style
copy_ignores =
    *.pyc
    *.md
; The build directory
build_dir = build
; For commands `run` and `git-diff`, whether the source .py will be removed
clean_py = 0
1. 项目第一次使用,使用init
$ encryptpy init .

查看build目录:

$ tree -a build
build
├── .encryptpy.cfg
└── package_a
    ├── __init__.cpython-38-x86_64-linux-gnu.so
    ├── main.cpython-38-x86_64-linux-gnu.so
    ├── setup.py
    └── utils.cpython-38-x86_64-linux-gnu.so

1 directory, 5 files
  1. 使用run
$ encryptpy run package_a/main.py

将package_a/main.py被重新编译为package_amain.cpython-38-x86_64-linux-gnu.so

  1. 使用git-diff
$ encryptpy git-diff 0.1 0.2

标记(或提交或分支)0.1 和 0.2 之间更改的文件将被编译。

  • 9
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值