[python] pycryptodome is commonly used to complete RSA asymmetric encryption and decryption, signature verification

1. Install pycryptodome

Install pycryptodome

pip install pycryptodome

2. Generate random public and private keys

Generate public and private keys, and export to PEM format, save the file

from Crypto.PublicKey import RSA

key = RSA.generate(2048)

pri_key = key.export_key()
with open("./pri_key.pem", "wb") as f:
    f.write(pri_key)

pub_key = key.public_key().export_key()
with open("./pub_key.pem", "wb") as f:
    f.write(pub_key)

3. Read the private key

The private key can be converted into a public key, so reading the private key is equivalent to reading the public key

from Crypto.PublicKey import RSA

with open("./pri_key.pem", "r") as f:
    pri_key_pem = f.read()

key = RSA.import_key(pri_key_pem)
pri_key = key.export_key()
print("pri_key:", pri_key)
pub_key = key.public_key().export_key()
print("pub_key:", pub_key)

4. Encryption and decryption

We use the previously generated key pair for encryption and decryption operations, use the public key to encrypt, and then use the private key to decrypt

import base64
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher
from Crypto.PublicKey import RSA


def get_key(path):
    with open(path) as f:
        pem_data = f.read()
        return RSA.importKey(pem_data)


def encrypt(msg, pub_path):
    key = get_key(pub_path)
    cipher = PKCS1_cipher.new(key)
    encrypt_msg = cipher.encrypt(msg.encode("utf-8"))
    return base64.b64encode(encrypt_msg).decode()


def decrypt(msg, pri_path):
    key = get_key(pri_path)
    cipher = PKCS1_cipher.new(key)
    decrypt_data = cipher.decrypt(base64.b64decode(msg), 0)
    return decrypt_data.decode("utf-8")


if __name__ == '__main__':
    original_msg = "hello world"
    encrypted_data = encrypt(original_msg, "./pub_key.pem")
    print("encrypt_data:", encrypted_data)
    decrypted_data = decrypt(encrypted_data, "./pri_key.pem")
    print("decrypt_data:", decrypted_data)

5. Encrypt long strings

Although the above method can encrypt and decrypt, there is a problem, that is, the error "ValueError: Plaintext is too long." will be reported if the string exceeds a certain length. The length of the string that can be encrypted is related to the number of public and private keys, max_length = (key digits/8 - 11), for example, if your secret key is 1024 bits, the length of the encrypted string cannot exceed 1024/8 - 11 = 117.

The solution is also very simple, we split the long string, then perform encryption and decryption operations and then splicing it into a complete string

import base64
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher
from Crypto.PublicKey import RSA

default_encoding = "utf-8"


def get_key(key_or_path):
    if "BEGIN PUBLIC KEY" in key_or_path or "BEGIN PRIVATE KEY" in key_or_path:
        pem_data = key_or_path
    else:
        with open(key_or_path) as f:
            pem_data = f.read()
    return RSA.importKey(pem_data)


def rsa_encrypt(msg, pub_path, max_length=100):
    """
    RSA加密
    :param msg: 加密字符串
    :param pub_path: 公钥路径
    :param max_length: 1024bit的秘钥不能超过117, 2048bit的秘钥不能超过245
    :return:
    """
    key = get_key(pub_path)
    cipher = PKCS1_cipher.new(key)
    res_byte = bytes()
    for i in range(0, len(msg), max_length):
        res_byte += cipher.encrypt(msg[i:i + max_length].encode(default_encoding))
    return base64.b64encode(res_byte).decode(default_encoding)


def rsa_decrypt(msg, pri_path, max_length=256):
    """
    RSA解密
    :param msg: 加密字符串
    :param pri_path: 私钥路径
    :param max_length: 1024bit的秘钥用128,2048bit的秘钥用256位
    :return:
    """
    key = get_key(pri_path)
    cipher = PKCS1_cipher.new(key)

    res_bytes = bytes()
    encrypt_data = base64.b64decode(msg)
    for i in range(0, len(encrypt_data), max_length):
        res_bytes += cipher.decrypt(encrypt_data[i:i + max_length], 0)
    return res_bytes.decode(default_encoding)


if __name__ == '__main__':
    original_msg = """很长的字符串'""" * 300
    encrypted_data = rsa_encrypt(original_msg, "./pub_key.pem")
    print("encrypt_data:", encrypted_data)
    decrypted_data = rsa_decrypt(encrypted_data, "./pri_key.pem")
    print("decrypt_data:", decrypted_data)

6. Signing and verifying signatures

The previous method is to use the public key to encrypt and the private key to decrypt, but there is another common scenario is to use the private key to encrypt and the public key to decrypt. The main purpose of this method is to generate a signature for some data and use the public key to verify the signature , to determine whether the data has been tampered with

import base64

from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5


def get_key(path):
    with open(path) as f:
        pem_data = f.read()
        return RSA.importKey(pem_data)


def generate_sign(un_sign_data, pri_key):
    signer = Signature_pkcs1_v1_5.new(pri_key)
    digest = SHA256.new()
    digest.update(un_sign_data.encode("utf-8"))
    signed_data = signer.sign(digest)
    return base64.b64encode(signed_data).decode("utf-8 ")


def verify_sign(un_sign_data, signature, pub_key):
    verifier = Signature_pkcs1_v1_5.new(pub_key)
    digest = SHA256.new()
    digest.update(un_sign_data.encode("utf-8"))
    return verifier.verify(digest, base64.b64decode(signature))


if __name__ == '__main__':
    data = "hello world" * 20
    signature = generate_sign(data, get_key("./pri_key.pem"))
    print("signature:", signature)
    is_ok = verify_sign(data, signature, get_key("./pub_key.pem"))
    print("is_ok:", is_ok)

Guess you like

Origin blog.csdn.net/qq_39147299/article/details/127754854