From 510c5f779cff34e2d790de721c5b54eb753f591d Mon Sep 17 00:00:00 2001 From: stirelshka8_BigARM Date: Sun, 2 Mar 2025 17:58:27 +0300 Subject: [PATCH] INIT --- .gitignore | 4 + README.md | 158 ++++++++++++++++++++++++++++++++++++ RSA_Crypto/decrypt.py | 86 ++++++++++++++++++++ RSA_Crypto/encrypt.py | 69 ++++++++++++++++ RSA_Crypto/generate_save.py | 104 ++++++++++++++++++++++++ main.py | 35 ++++++++ 6 files changed, 456 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 RSA_Crypto/decrypt.py create mode 100644 RSA_Crypto/encrypt.py create mode 100644 RSA_Crypto/generate_save.py create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6dc124 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.venv/ +/keys/ +*.pem +/output.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..1352179 --- /dev/null +++ b/README.md @@ -0,0 +1,158 @@ +*** +# Шифрование/дешифрование с использованием RSA ключей (закрытый и открытый ключи). + +## Описание модулей: + +### **1. Генерация и сохранение ключей** + +Класс RSAKeyPair предназначен для генерации и сохранения пары ключей RSA (приватного и публичного). Вот подробное описание его работы и принципа функционирования: + +▎Описание класса RSAKeyPair + +1. Инициализация: + + • Конструктор класса (__init__) принимает два параметра: имена файлов для сохранения приватного и публичного ключей. + + • Он инициализирует атрибуты для хранения ключей и имен файлов. + +2. Генерация приватного ключа: + + • Метод generate_private_key создает приватный ключ RSA с использованием алгоритма RSA с длиной ключа 2048 бит и публичным экспонентом 65537. + + • Этот метод использует библиотеку cryptography для генерации ключа. + +3. Генерация публичного ключа: + + • Метод generate_public_key извлекает публичный ключ из ранее сгенерированного приватного ключа. + + • Если приватный ключ еще не был сгенерирован, метод вызывает исключение ValueError. + +4. Сохранение приватного ключа: + + • Метод save_private_key сохраняет приватный ключ в указанный файл в формате PEM без шифрования. + + • Если приватный ключ не был сгенерирован, метод вызывает исключение ValueError. + +5. Сохранение публичного ключа: + + • Метод save_public_key сохраняет публичный ключ в указанный файл в формате PEM. + + • Если публичный ключ не был сгенерирован, метод вызывает исключение ValueError. + +6. Генерация и сохранение ключей: + + • Метод generate_and_save_keys проверяет, существуют ли файлы для приватного и публичного ключей. Если оба файла отсутствуют, он генерирует приватный ключ, сохраняет его, затем генерирует публичный ключ и также сохраняет его. + + • Если файлы уже существуют, метод просто выводит сообщение в консоль и не вызывает исключения. + +▎Принцип работы + +1. Создание экземпляра класса: Пользователь создает экземпляр класса RSAKeyPair, указывая файлы для сохранения ключей. + +2. Генерация ключей: Пользователь вызывает метод generate_and_save_keys, который проверяет наличие файлов. Если они не существуют, вызываются методы для генерации и сохранения ключей. + +3. Работа с файлами: Приватный и публичный ключи сохраняются в файлы в формате PEM, что позволяет легко использовать их в других приложениях или библиотеках. + +4. Обработка ошибок: Класс обрабатывает случаи, когда попытка сохранить ключи осуществляется до их генерации, или когда файлы уже существуют, что делает его удобным для использования в различных сценариях. + +Таким образом, класс RSAKeyPair предоставляет простой интерфейс для работы с RSA-ключами, включая их генерацию и сохранение в файлы. + +### **2. Шифрование** + +Класс RSAEncryptor предназначен для шифрования сообщений с использованием открытого ключа RSA. Он загружает открытый ключ из файла и предоставляет метод для шифрования сообщений. Шифрование выполняется с использованием схемы OAEP (Optimal Asymmetric Encryption Padding) с хешированием SHA-256. + +▎Принцип работы + +1. Инициализация: + + • Класс RSAEncryptor инициализируется с именем файла, содержащим открытый ключ. + + • В методе init вызывается функция load_public_key, которая загружает открытый ключ из указанного файла. + +2. Загрузка открытого ключа: + + • Метод load_public_key открывает файл с открытым ключом, считывает его содержимое и десериализует его в объект открытого ключа RSA с помощью функции serialization.load_pem_public_key. + +3. Шифрование сообщения: + + • Метод encrypt_message принимает строку (сообщение) в качестве параметра. + + • Сообщение кодируется в байты и затем шифруется с использованием открытого ключа и схемы OAEP. + + • OAEP использует MGF1 (Mask Generation Function) с алгоритмом SHA-256 для обеспечения безопасности шифрования. + + • Защищенное сообщение затем кодируется в Base64 для удобства передачи и хранения. + +4. Возврат зашифрованного сообщения: + + • Метод возвращает зашифрованное сообщение в виде строки, закодированной в Base64. + +▎Пример использования + +~~~ python +# Создание экземпляра RSAEncryptor +encryptor = RSAEncryptor('path/to/public_key.pem') + +# Шифрование сообщения +encrypted_message = encryptor.encrypt_message('Hello, World!') +print(encrypted_message) +~~~ + +▎Замечания + +• Для работы данного класса необходимо, чтобы файл с открытым ключом был доступен и содержал корректный PEM-формат. + +• RSA обычно используется для шифрования небольших объемов данных, поэтому для больших сообщений может потребоваться использование симметричного шифрования (например, AES) в сочетании с RSA для безопасной передачи симметричного ключа. + +### **3. Расшифрование** + +Класс RSADecryptor предназначен для расшифровки сообщений, зашифрованных с использованием алгоритма RSA. Он загружает закрытый ключ из файла и предоставляет метод для расшифровки сообщений, которые были зашифрованы с использованием схемы OAEP (Optimal Asymmetric Encryption Padding) с хешированием SHA-256. + +▎Принцип работы + +1. Инициализация: + + • Класс RSADecryptor инициализируется с именем файла, содержащим закрытый ключ. + + • В методе init вызывается функция load_private_key, которая загружает закрытый ключ из указанного файла. + +2. Загрузка закрытого ключа: + + • Метод load_private_key открывает файл с закрытым ключом, считывает его содержимое и десериализует его в объект закрытого ключа RSA с помощью функции serialization.load_pem_private_key. + + • Важно, чтобы файл содержал закрытый ключ в формате PEM. + +3. Расшифровка сообщения: + + • Метод decrypt_message принимает строку (зашифрованное сообщение) в качестве параметра. + + • Зашифрованное сообщение декодируется из Base64 в байты. + + • Затем сообщение расшифровывается с использованием закрытого ключа и схемы OAEP. + + • OAEP использует MGF1 (Mask Generation Function) с алгоритмом SHA-256 для обеспечения безопасности расшифровки. + + • Расшифрованное сообщение возвращается в виде строки. + +4. Возврат расшифрованного сообщения: + + • Метод возвращает расшифрованное сообщение как строку. + +▎Пример использования + +~~~ python +# Создание экземпляра RSADecryptor +decryptor = RSADecryptor('path/to/private_key.pem') + +# Расшифровка сообщения +decrypted_message = decryptor.decrypt_message(encrypted_message) +print(decrypted_message) +~~~ + +▎Замечания + +• Для работы данного класса необходимо, чтобы файл с закрытым ключом был доступен и содержал корректный PEM-формат. + +• При использовании закрытого ключа важно обеспечить его безопасность, чтобы предотвратить несанкционированный доступ к данным. + +• RSA обычно используется для расшифровки небольших объемов данных, поэтому для больших сообщений может потребоваться использование симметричного шифрования (например, AES) в сочетании с RSA для безопасной передачи симметричного ключа. diff --git a/RSA_Crypto/decrypt.py b/RSA_Crypto/decrypt.py new file mode 100644 index 0000000..d18dcc8 --- /dev/null +++ b/RSA_Crypto/decrypt.py @@ -0,0 +1,86 @@ +""" +Decrypts messages encrypted with RSA. +""" + +import base64 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import padding + + +class RSADecryptor: + """ + Module for decrypting messages encrypted with RSA. + + Provides + -------- + + * :class:`RSADecryptor` - class for decrypting messages. + """ + def __init__(self, private_key_file): + """ + Initialize an RSADecryptor with a file containing a private RSA key. + + Parameters + ---------- + private_key_file : str + The path to the private key file, which should be in PEM format. + + Attributes + ---------- + private_key_file : str + The path to the private key file, which should be in PEM format. + private_key : cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey + The loaded private key. + """ + self.private_key_file = private_key_file + self.private_key = self.load_private_key() + + def load_private_key(self): + """ + Load a private key from a file. + + The file should be in PEM format and contain the private key. + + Returns + ------- + private_key : cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey + The loaded private key. + """ + with open(self.private_key_file, 'rb') as f: + private_key_data = f.read() + + private_key = serialization.load_pem_private_key( + private_key_data, + password=None, + backend=default_backend() + ) + return private_key + + def decrypt_message(self, encrypted_message): + """ + Decrypt an encrypted message. + + Parameters + ---------- + encrypted_message : str + The encrypted message as a base64 encoded string. + + Returns + ------- + decrypted_message : str + The decrypted message as a string. + """ + encrypted_bytes = base64.b64decode(encrypted_message) + try: + decrypted = self.private_key.decrypt( + encrypted_bytes, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) + return decrypted.decode() + except ValueError: + return None diff --git a/RSA_Crypto/encrypt.py b/RSA_Crypto/encrypt.py new file mode 100644 index 0000000..9c8c10c --- /dev/null +++ b/RSA_Crypto/encrypt.py @@ -0,0 +1,69 @@ +""" +Encrypt messages encrypted with RSA. +""" + +import base64 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import padding + + +class RSAEncryptor: + """ + Module for encrypting messages encrypted with RSA. + + Provides + -------- + + * :class:`RSAEncryptor` - class for encrypting messages. + """ + def __init__(self, public_key_file): + self.public_key_file = public_key_file + self.public_key = self.load_public_key() + + def load_public_key(self): + """ + Loads a public key from a file. + + Parameters + ---------- + self : `RSAEncryptor` + The object itself. + + Returns + ------- + `cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` + The loaded key. + """ + with open(self.public_key_file, 'rb') as f: + public_key_data = f.read() + + public_key = serialization.load_pem_public_key( + public_key_data, + backend=default_backend() + ) + return public_key + + def encrypt_message(self, message): + """ + Encrypts a message using RSA public key encryption. + + Parameters + ---------- + message : str + The message to be encrypted. + + Returns + ------- + str + The encrypted message encoded in Base64. + """ + encrypted = self.public_key.encrypt( + message.encode(), + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) + return base64.b64encode(encrypted).decode('utf-8') diff --git a/RSA_Crypto/generate_save.py b/RSA_Crypto/generate_save.py new file mode 100644 index 0000000..28c710d --- /dev/null +++ b/RSA_Crypto/generate_save.py @@ -0,0 +1,104 @@ +""" +Generate and save RSA key pair +""" + +import os +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa + + +class RSAKeyPair: + """ + Class for generating and saving RSA key pair + """ + + def __init__(self, private_key_file, public_key_file): + self.private_key_file = private_key_file + self.public_key_file = public_key_file + self.private_key = None + self.public_key = None + + def generate_private_key(self): + """ + Generate RSA private key. + + The generated key has a 2048 bit length and a public exponent of 65537. + """ + self.private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=default_backend() + ) + + def generate_public_key(self): + """ + Generate the public key from the private key. + + Raises: + ValueError: Private key must be generated first. + """ + if self.private_key is None: + raise ValueError("Private key must be generated first.") + self.public_key = self.private_key.public_key() + + def save_private_key(self): + """ + Save the generated private key to a file. + + The private key is saved in PEM format with the Traditional OpenSSL + format and no encryption. If the private key has not been generated yet, + a ValueError is raised. + + Raises: + ValueError: Private key must be generated before saving. + """ + if self.private_key is None: + raise ValueError("Private key must be generated before saving.") + with open(self.private_key_file, 'wb') as f: + f.write(self.private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption() + )) + + def save_public_key(self): + """ + Save the generated public key to a file. + + The public key is saved in PEM format with the SubjectPublicKeyInfo + format. If the public key has not been generated yet, a ValueError is + raised. + + Raises: + ValueError: Public key must be generated before saving. + """ + if self.public_key is None: + raise ValueError("Public key must be generated before saving.") + with open(self.public_key_file, 'wb') as f: + f.write(self.public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo + )) + + def generate_and_save_keys(self): + """ + Generate and save the private and public keys to the specified files. + + This method first checks if the key files already exist. If not, it + generates the private key and saves it to the private key file, and + generates the public key based on the private key and saves it to the + public key file. If the files already exist, it simply prints a message + to the console. + + This method does not raise any exceptions if the files already exist. + """ + if not os.path.exists(self.private_key_file) and not os.path.exists(self.public_key_file): + try: + self.generate_private_key() + self.save_private_key() + self.generate_public_key() + self.save_public_key() + except ValueError as e: + print(e) + diff --git a/main.py b/main.py new file mode 100644 index 0000000..2055cea --- /dev/null +++ b/main.py @@ -0,0 +1,35 @@ +import os +import json +from RSA_Crypto.encrypt import RSAEncryptor +from RSA_Crypto.decrypt import RSADecryptor +from RSA_Crypto.generate_save import RSAKeyPair + +KEYS_DIR = 'keys' +PRIVATE_KEY_FILE = f'{KEYS_DIR}/private-key.pem' +PUBLIC_KEY_FILE = f'{KEYS_DIR}/public-key.pem' + +if not os.path.isdir(KEYS_DIR): + os.makedirs(KEYS_DIR, exist_ok=True) + +key_pair = RSAKeyPair(PRIVATE_KEY_FILE, PUBLIC_KEY_FILE) +key_pair.generate_and_save_keys() + +message = '{"organization": "ООО Рога, Копыта & Ко", "license_expiration_date": "2024-01-01", "license_number": "1234567890", "count_licenses": 100}' +rsa_encryptor = RSAEncryptor(PUBLIC_KEY_FILE) +encrypted_message = rsa_encryptor.encrypt_message(message) + +with open('output.txt', 'w') as f: + f.write(encrypted_message) + +rsa_decryptor = RSADecryptor(PRIVATE_KEY_FILE) + +with open('output.txt', 'r') as f: + encrypted_message_from_file = f.read() + +decrypted_message = rsa_decryptor.decrypt_message(encrypted_message_from_file) + +try: + decrypted_message_dict = json.loads(decrypted_message) + print(decrypted_message_dict['organization']) +except json.JSONDecodeError: + print("Ошибка: Не удалось декодировать сообщение как JSON.")