[python]: use the key encryption algorithm AES, cpython to realize project file encryption

1. Detailed case of AES encryption and decryption

The following is a detailed case of using Python to implement AES encryption and decryption.

1.1. Import the necessary libraries:

import base64
import hashlib
import os
from Crypto import Random
from Crypto.Cipher import AES

1.2. Define encryption and decryption classes:

class AESCipher:
    def __init__(self, key):
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw)).decode('utf-8')

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

    def _pad(self, s):
        return s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)

    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s)-1:])]

1.3. Test encryption and decryption:

key = os.urandom(32)  # 生成一个随机的32 bytes的key
cipher = AESCipher(key)

plaintext = 'hello world'
encrypted = cipher.encrypt(plaintext)
decrypted = cipher.decrypt(encrypted)

print('plaintext: ', plaintext)
print('encrypted: ', encrypted)
print('decrypted: ', decrypted)

output:

plaintext:  hello world
encrypted:  PAEte9EbH5Ciz3n5xZp6uTqwEqLr6aWM9oE4iUpvcAI=
decrypted:  hello world

Encryption and decryption both work fine. Now let's break down how the defined classes and methods work.

First, you need to provide a key for encryption when initializing the class, which will be generated from the password string using SHA-256 hash:

def __init__(self, key):
    self.key = hashlib.sha256(key.encode()).digest()

Next the encryption method is defined. It uses the random module in python's Crypto library to generate a random vector (iv) which will be stored with the ciphertext.

Then, choose an AES encryptor that uses CBC mode. In this mode, each block is encrypted with the previous one, making it harder to access the ciphertext.

Finally, the function encodes the ciphertext using base64 encoding. Encoding is always necessary, because raw Bytes encrypted text usually only contains ASCII characters, which can cause some problems (for example, when it is used for network transmission or needs to be read on different computers).

def encrypt(self, raw):
    raw = self._pad(raw)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(self.key, AES.MODE_CBC, iv)
    return base64.b64encode(iv + cipher.encrypt(raw)).decode('utf-8')

The decryption function conforms to the encryption function. It first decodes the BASE-64 encoded (encrypted) ciphertext, then reinitializes the AES encryptor with the same IV and key.

The decryptor does the opposite. It removes the IV and tries to decrypt each block from it. Finally, it also removes padding.

def decrypt(self, enc):
    enc = base64.b64decode(enc)
    iv = enc[:AES.block_size]
    cipher = AES.new(self.key, AES.MODE_CBC, iv)
    return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

Regarding padding and defilling, both functions deserve an explanation:

def _pad(self, s):
    return s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)

@staticmethod
def _unpad(s):
    return s[:-ord(s[len(s)-1:])]

The PKCS #7 filling mode is used here, which is a commonly used filling scheme. If filling is required, characters corresponding to the ASCII value are added until filling is complete.
To decrypt, you must first obtain the final character encoding value, then use this value to determine how many characters need to be removed, and finally remove these characters.

2. Encrypt a single .py file

The implementation steps are as follows:

  1. Install the PyCryptodome module, which supports the implementation of the AES encryption algorithm. It can be installed with the following command:

    pip install pycryptodome

  2. Write a py file encryption script to encrypt the py file that needs to be encrypted. For example:

from Crypto.Cipher import AES
import os


# 加密算法,key 为 16、24 或 32 个字符的字符串

def encrypt_file(key, in_filename, out_filename=None, chunksize=64 * 1024):
    if not out_filename:
        out_filename = in_filename + '.enc'

    iv = os.urandom(AES.block_size)  # 随机生成密钥

    encryptor = AES.new(key, AES.MODE_CBC, iv)

    filesize = os.path.getsize(in_filename)

    with open(in_filename, 'rb') as infile:
        with open(out_filename, 'wb') as outfile:
            outfile.write(filesize.to_bytes(8, 'big'))  # 写入文件大小
            outfile.write(iv)  # 写入密钥向量

            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                elif len(chunk) % AES.block_size != 0:
                    chunk += b' ' * (AES.block_size - len(chunk) % AES.block_size)

                outfile.write(encryptor.encrypt(chunk))

Usage: encrypt_file('mypassword', 'mytest.py')

  1. After the encryption is complete, the original py file can be deleted and only the encrypted file is kept. At the same time, you can rename the encrypted file with a suffix of .pyc, so that the Python interpreter thinks that the file is a compiled bytecode file, so it can run normally. For example:

    # 将加密后的 mytest.py.enc 文件重命名为 mytest.pyc
    os.rename('mytest.py.enc', 'mytest.pyc')
    
  2. In a command window, the encrypted .pyc file can be run with the following command:

    python mytest.pyc

    NOTE: The original filename must be stored in the encrypted file when encrypting in order to get the filename when decrypting. In addition, the decryption operation requires the same password used for encryption. The following decryption methods are available:

    from Crypto.Cipher import AES
    import os
    
    # 解密算法,key 为 16、24 或 32 个字符的字符串
    def decrypt_file(key, in_filename, out_filename=None, chunksize=64 * 1024):
        if not out_filename:
            out_filename = os.path.splitext(in_filename)[0]
    
        with open(in_filename, 'rb') as infile:
            orig_size = int.from_bytes(infile.read(8), 'big')
            iv = infile.read(AES.block_size)
    
            decryptor = AES.new(key, AES.MODE_CBC, iv)
    
            with open(out_filename, 'wb') as outfile:
                while True:
                    chunk = infile.read(chunksize)
                    if len(chunk) == 0:
                        break
                    outfile.write(decryptor.decrypt(chunk))
    
                outfile.truncate(orig_size)
    
    # 使用方式:decrypt_file('mypassword', 'mytest.pyc.enc')
    

    After decryption, the original py file will be obtained (if the file name before encryption is mytest.py, the decrypted file name is mytest.py).

If the above steps are not easy to understand, you can consider using third-party encryption tools, such as pyarmor or cython, etc. These tools can use more advanced encryption methods and provide more convenient interfaces. For example, using pyarmor, the mytest.py file can be encrypted directly with the following command:

pyarmor obfuscate mytest.py

The encrypted file is located in the dist directory and can be run with the following command:

python dist/mytest.py

Note: If you use these tools for encryption, you need to know their authorization mode and usage method in advance.

3. Use cpthon to encrypt files

Refer to my other link:

Reference link:

Portal 1:
Portal 2:
How to encrypt Python code (Script House)
Encrypt python's py file (source code) into so file (linux)

Guess you like

Origin blog.csdn.net/weixin_44322778/article/details/130746508