diff --git a/decrypted_text.txt b/decrypted_text.txt new file mode 100644 index 00000000..1739e78c --- /dev/null +++ b/decrypted_text.txt @@ -0,0 +1 @@ +Она несла в руках отвратительные, тревожные желтые цветы. Черт их знает, как их зовут, но они первые почему-то появляются в Москве. И эти цветы очень отчетливо выделялись на черном ее весеннем пальто. Она несла желтые цветы! Нехороший цвет. Она повернула с Тверской в переулок и тут обернулась. Ну, Тверскую вы знаете? По Тверской шли тысячи людей, но я вам ручаюсь, что увидела она меня одного и поглядела не то что тревожно, а даже как будто болезненно. И меня поразила не столько ее красота, сколько необыкновенное, никем не виданное одиночество в глазах! \ No newline at end of file diff --git a/encrypted_text.txt b/encrypted_text.txt new file mode 100644 index 00000000..6ec96a8e Binary files /dev/null and b/encrypted_text.txt differ diff --git a/lab_3/asymmetrical.py b/lab_3/asymmetrical.py new file mode 100644 index 00000000..bf9f1fd9 --- /dev/null +++ b/lab_3/asymmetrical.py @@ -0,0 +1,74 @@ +from cryptography.hazmat.primitives import padding, hashes +from cryptography.hazmat.primitives.asymmetric import rsa, padding + + +class AsymmetricCryptography: + """ + Class for asymmetric encryption using RSA + """ + def __init__(self, private_key_path: str, public_key_path: str) -> None: + """ + Initializes AsymmetricCryptography class object + :param private_key_path: path for saving private key + :param public_key_path: path for saving public key + """ + self.private_key_path = private_key_path + self.public_key_path = public_key_path + + @staticmethod + def generate_key() -> tuple[rsa.RSAPrivateKey, rsa.RSAPublicKey]: + """ + Generates a pair of RSA keys + :return: tuple of private and public keys + """ + try: + private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + public_key = private_key.public_key() + return private_key, public_key + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def encrypt(data: bytes, public_key: rsa.RSAPublicKey) -> bytes: + """ + Encrypts data using a public RSA key + :param data: data to encrypt + :param public_key: public key for encryption + :return: encrypted data + """ + try: + c_data = public_key.encrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None, + ) + ) + return c_data + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def decrypt(data: bytes, private_key: rsa.RSAPrivateKey) -> bytes: + """ + Decrypts data using a private RSA key + :param data: encrypted data + :param private_key: private key for decryption + :return: decrypted data + """ + try: + dc_data = private_key.decrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None, + ) + ) + return dc_data + except Exception as e: + print("Error:", e) + raise \ No newline at end of file diff --git a/lab_3/crypto_system.py b/lab_3/crypto_system.py new file mode 100644 index 00000000..b501fd37 --- /dev/null +++ b/lab_3/crypto_system.py @@ -0,0 +1,81 @@ +from cryptography.hazmat.primitives import padding, hashes +from cryptography.hazmat.primitives.asymmetric import padding + +from asymmetrical import AsymmetricCryptography +from serialize import Serialization +from symmetrical import SymmetricCryptography +from work_file import * + + +class HybridCryptoSystem: + """ + Class implementing a hybrid crypto system using SEED (symmetric) and RSA (asymmetric) algorithms + """ + + def __init__(self, symmetric_key_path: str, private_key_path: str, public_key_path: str) -> None: + """ + Initializes HybridCryptoSystem class object + :param symmetric_key_path: path for saving symmetrical key + :param private_key_path: path for saving private key + :param public_key_path: path for saving public key + """ + self.block_size = 128 + self.symmetric = SymmetricCryptography(symmetric_key_path) + self.asymmetric = AsymmetricCryptography(private_key_path, public_key_path) + + def generate_keys(self, size: int) -> None: + """ + Generates key for hybrid method + :param size: size of keys + """ + try: + symmetric_key = self.symmetric.generate_key(size) + asymmetric_key = self.asymmetric.generate_key() + private_key, public_key = asymmetric_key + + Serialization.save_private_key(self.asymmetric.private_key_path, private_key) + Serialization.save_public_key(self.asymmetric.public_key_path, public_key) + write_bytes(public_key.encrypt(symmetric_key, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None, + ) + ), + self.symmetric.key_path) + except Exception as e: + print("Error:", e) + raise + + def encrypt(self, data_path: str, encrypted_data_path: str) -> None: + """ + Encrypts a file using a hybrid system + :param data_path: path to file with data + :param encrypted_data_path: path to file for saving encrypted data + """ + try: + text = bytes(read_txt(data_path), "UTF-8") + key = Serialization.load_private_key(self.asymmetric.private_key_path) + symmetric_key = self.asymmetric.decrypt(read_bytes(self.symmetric.key_path), key) + c_text = self.symmetric.encrypt(text, symmetric_key) + write_bytes(c_text, encrypted_data_path) + except Exception as e: + print("Error:", e) + raise + + def decrypt(self, data_path: str, decrypted_data_path: str) -> None: + """ + Decrypts a file using a hybrid system + :param data_path: path to file with data + :param decrypted_data_path: path to file for saving decrypted data + :return: + """ + try: + c_data = read_bytes(data_path) + key = Serialization.load_private_key(self.asymmetric.private_key_path) + symmetric_key = self.asymmetric.decrypt(read_bytes(self.symmetric.key_path), key) + dc_data = self.symmetric.decrypt(c_data, symmetric_key) + write_bytes(dc_data, decrypted_data_path) + except Exception as e: + print("Error:", e) + raise \ No newline at end of file diff --git a/lab_3/main.py b/lab_3/main.py new file mode 100644 index 00000000..a8ce664f --- /dev/null +++ b/lab_3/main.py @@ -0,0 +1,42 @@ +import argparse + +from crypto_system import HybridCryptoSystem +from work_file import * + + +def main(): + paths = read_json("settings.json") + + main_parser = argparse.ArgumentParser() + group = main_parser.add_mutually_exclusive_group(required=True) + group.add_argument("-gen", "--generation", help="key generation mode", nargs='?', const=32, default=None, type=int, + choices=[16, 24, 32]) + group.add_argument("-enc", "--encryption", help="encryption mode", action="store_true") + group.add_argument("-dec", "--decryption", help="decryption mode", action="store_true") + + main_parser.add_argument("--symmetric-key", help="path to symmetric key", default=paths["symmetric_key"]) + main_parser.add_argument("--secret-key", help="path to secret key", default=paths["secret_key"]) + main_parser.add_argument("--public-key", help="path to public key", default=paths["public_key"]) + + args = main_parser.parse_args() + + args.generation_flag = args.generation is not None + + if args.generation_flag and args.generation is None: + args.generation = 32 + + match (args.generation_flag, args.encryption, args.decryption): + case (True, False, False): + key_size = args.generation + h = HybridCryptoSystem(args.symmetric_key, args.secret_key, args.public_key) + h.generate_keys(key_size) + case (False, True, False): + h = HybridCryptoSystem(args.symmetric_key, args.secret_key, args.public_key) + h.encrypt(paths["initial_file"], paths["encrypted_file"]) + case (False, False, True): + h = HybridCryptoSystem(args.symmetric_key, args.secret_key, args.public_key) + h.decrypt(paths["encrypted_file"], paths["decrypted_file"]) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/lab_3/serialize.py b/lab_3/serialize.py new file mode 100644 index 00000000..550bcd91 --- /dev/null +++ b/lab_3/serialize.py @@ -0,0 +1,112 @@ +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.serialization import ( + load_pem_public_key, + load_pem_private_key, +) + + +class Serialization: + """ + Class for serializing and deserializing cryptographic keys + """ + + @staticmethod + def save_symmetric_key(file_path: str, key: bytes) -> None: + """ + Saves the symmetric key to file + :param file_path: path to file to save + :param key: key to save + """ + try: + with open(file_path, "wb") as key_file: + key_file.write(key) + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def load_symmetric_key(file_path: str) -> bytes: + """ + Loads the symmetric key from file + :param file_path: path to file with key + :return: key + """ + try: + with open(file_path, "rb") as key_file: + return key_file.read() + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def save_private_key(path: str, private_key: rsa.RSAPrivateKey) -> None: + """ + Saves the private RSA key to a file + :param path: path to file to save + :param private_key: the RSA private key + """ + try: + with open(path, "wb") as private_out: + private_out.write( + private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + ) + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def save_public_key(path: str, public_key: rsa.RSAPublicKey) -> None: + """ + Saves the public RSA key to a file + :param path: path to file to save + :param public_key: the RSA public key + """ + try: + with open(path, "wb") as public_out: + public_out.write( + public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + ) + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def load_private_key(path: str) -> rsa.RSAPrivateKey: + """ + Loads a private RSA key from a file + :param path: the path to the file that contains the key + :return: the RSA private key + """ + try: + with open(path, "rb") as pem_in: + private_bytes = pem_in.read() + d_private_key = load_pem_private_key( + private_bytes, + password=None, + ) + return d_private_key + except Exception as e: + print("Error:", e) + + @staticmethod + def load_public_key(path: str) -> rsa.RSAPublicKey: + """ + Loads a public RSA key from a file + :param path: the path to the file that contains the key + :return: the RSA public key + """ + try: + with open(path, "rb") as pem_in: + public_bytes = pem_in.read() + d_public_key = load_pem_public_key(public_bytes) + return d_public_key + except Exception as e: + print("Error:", e) \ No newline at end of file diff --git a/lab_3/settings.json b/lab_3/settings.json new file mode 100644 index 00000000..5a9ea02c --- /dev/null +++ b/lab_3/settings.json @@ -0,0 +1,8 @@ +{ + "symmetric_key": "symmetric_key.txt", + "public_key": "public.pem", + "secret_key": "private.pem", + "initial_file": "text.txt", + "encrypted_file": "encrypted_text.txt", + "decrypted_file": "decrypted_text.txt" +} \ No newline at end of file diff --git a/lab_3/symmetrical.py b/lab_3/symmetrical.py new file mode 100644 index 00000000..d4a88b6f --- /dev/null +++ b/lab_3/symmetrical.py @@ -0,0 +1,75 @@ +import os + +from cryptography.hazmat.primitives import padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + + +class SymmetricCryptography: + """ + class for symmetric encryption using the Camellia algorithm + """ + def __init__(self, key_path: str) -> None: + """ + initializes SymmetricCryptography class object + :param key_path: path to key + """ + self.key_path = key_path + + @staticmethod + def generate_key(size: int) -> bytes: + """ + generate key for symmetrical method + :param size: size of key + :return: generated key + """ + try: + key = os.urandom(size) + return key + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def encrypt(data: bytes, key: bytes) -> bytes: + """ + encrypts data using the Camellia algorithm in CBC mode + :param data: data to encrypt + :param key: key for encryption + :return: encrypted data in iv + ciphertext format + """ + if len(key) not in {16, 24, 32}: + raise ValueError("the size of key must be 16, 24, or 32 bytes") + try: + iv = os.urandom(16) + cipher = Cipher(algorithms.Camellia(key), modes.CBC(iv)) + encryptor = cipher.encryptor() + padder = padding.PKCS7(algorithms.Camellia.block_size).padder() + padded_text = padder.update(data) + padder.finalize() + c_text = iv + encryptor.update(padded_text) + encryptor.finalize() + return c_text + except Exception as e: + print("Error:", e) + raise + + @staticmethod + def decrypt(data: bytes, key: bytes) -> bytes: + """ + decrypts data encrypted with the encrypt method + :param data: encrypted data in iv + ciphertext format + :param key: key for decryption + :return: decrypted data + """ + if len(key) not in {16, 24, 32}: + raise ValueError("the size of key must be 16, 24, or 32 bytes") + try: + iv = data[:16] + data = data[16:] + cipher = Cipher(algorithms.Camellia(key), modes.CBC(iv)) + decryptor = cipher.decryptor() + dc_data = decryptor.update(data) + decryptor.finalize() + unpadder = padding.PKCS7(algorithms.Camellia.block_size).unpadder() + unpadded_dc_data = unpadder.update(dc_data) + unpadder.finalize() + return unpadded_dc_data + except Exception as e: + print("Error:", e) + raise \ No newline at end of file diff --git a/lab_3/text.txt b/lab_3/text.txt new file mode 100644 index 00000000..1739e78c --- /dev/null +++ b/lab_3/text.txt @@ -0,0 +1 @@ +Она несла в руках отвратительные, тревожные желтые цветы. Черт их знает, как их зовут, но они первые почему-то появляются в Москве. И эти цветы очень отчетливо выделялись на черном ее весеннем пальто. Она несла желтые цветы! Нехороший цвет. Она повернула с Тверской в переулок и тут обернулась. Ну, Тверскую вы знаете? По Тверской шли тысячи людей, но я вам ручаюсь, что увидела она меня одного и поглядела не то что тревожно, а даже как будто болезненно. И меня поразила не столько ее красота, сколько необыкновенное, никем не виданное одиночество в глазах! \ No newline at end of file diff --git a/lab_3/work_file.py b/lab_3/work_file.py new file mode 100644 index 00000000..ca111e43 --- /dev/null +++ b/lab_3/work_file.py @@ -0,0 +1,75 @@ +import json + + +def read_bytes(path: str) -> bytes: + """ + Reads bytes from txt file + :param path: path to txt file + :return: bytes + """ + try: + with open(path, "rb") as file: + data = file.read() + return data + except Exception as e: + print("Error:", e) + + +def write_bytes(data: bytes, path: str) -> None: + """ + Writes bytes into txt file + :param data: bytes object that is needed to write + :param path: path to txt file + """ + try: + with open(path, "wb") as file: + file.write(data) + except Exception as e: + print("Error:", e) + + +def read_txt(filename: str) -> str: + """ + Read and return the content of a text file + :param filename: path to the text file to be read + :return: content of the file as a string + """ + try: + with open(filename, 'r', encoding='utf-8') as file: + file = file.read().strip() + return file + except FileNotFoundError: + print(f"file {filename} not found") + except Exception as e: + print(f"error: {e}") + + +def write_txt(data: str, filename: str) -> None: + """ + Writes string into txt file + :param data: object that is needed to write + :param filename: path to the output txt file + :return: + """ + try: + with open(filename, 'w', encoding='utf-8') as f: + f.write(data) + except Exception as e: + raise Exception(f"An error occurred when saving the file: {e}") + + +def read_json(filename: str) -> dict: + """ + Read and parse a JSON file into a dictionary + :param filename: path to the text file to be read + :return: parsed JSON data as a dictionary + """ + try: + with open(filename, "r", encoding="utf-8") as file: + return json.load(file) + except FileNotFoundError: + print(f"file {filename} not found") + except json.JSONDecodeError: + print(f"file {filename} isn't correct JSON.") + except Exception as e: + print(f"error: {e}") \ No newline at end of file diff --git a/private.pem b/private.pem new file mode 100644 index 00000000..3c30684d --- /dev/null +++ b/private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEApAJlIIqbo1GPAIuukUzEwaKfQ0jyFxHDos2iPFSgNL6BSW9F +0aMyqi80Y98+6YOJSl8ssPIRq6NOCbUngy4vlUxMr7raw+JXDr0jiQIjogQZpeSM +rF4kHMB3f99bd4VOMdYVXk+GPoEwK0ula0rVRDdqhAWyPOqTibWBEApRtMkT7Fgr +B0wP0bGP4GS4vBkLX2u+ty7yHfJiZ+KaZyiStveFkc0WhWuaUv6pejV8Oqx84Byu +0iGk2IpCxDrNkTo8/Z4UVHGrrhAolNIJUd2vtgJcRLNAhiRMk2oNPcOJWu6l6ZYx +YzY+D9Exmw+vb5DKpQC7+aRjb0HsNcDnlyYZzQIDAQABAoIBABRPNZ2jfGVrfpVX +Nr1Ib974l9c5q7zelsbMxwfIhPQzGQhybodouafoY4jwhrZc8v3SLlMVDsod1VaP +Z76vU/YddiuWrGtwVk59bm6TZL0128U2uZLIr5kd3k5n5sg0qjuyon/Ewj9wOPpL +ikbwg9tIL3j8yZcQh0eYwcwuMvL7dpYfBaK+rGKrqcN2R2tGHTkTsct3DYhuZycY +v25IkE09PmWeKhlIEikHoXI8AiAjJjK+tyZyN8dGcerWTNpvwa0dRZN3KAgruH62 +/tgmN93EFZ4vDO/pknsRTZATmwRvYmPUK3I8giKgCsn5WxJH9ugI69gb9RoJeQbk +sv43mvMCgYEA2S5zBBBr06uc6xZ2O1wYjnjcJlrcV4CkJ99U7tlaL0DyrbQPJuy0 +1zl8BT1uBCvfj/auKOYe5qSoXC4XQUasnhqCQIWtlEnWf4MEm1lhAfiVK2Wur3Oy +XvE0IwwVi+ud3R6CHWWkKktx6lh2NezlB0uGHD+ng4awN27Z831Ye3cCgYEAwVL0 +oeqsl6YSO41ZUbc0sbtU6RgMm0sOHJT0HDrsSkNbXZ6agSkiqYOJyYL3oE3ZdC2m +TBh6Gb7IK4i1tCv/srOCc57r2nf20/jh7oKeZjqY0hpQ+sdykAFC4CKYlYKLiqjV +vMHHgINGPBO8SFwTNSWH0DmvvdMh3OPeZItIHdsCgYAYUTUbnWMVXj/BqtAuMU+m +lC3CNaY6PfMja8QswjVw2QrTHDgeGZ7eAle6lng9C1SK4An8FlgbZxBa0eZUrr8h +LMKsysr2sZXMzTS02DseZ2D8bPQ0PPYZ31QBIJdBBW61lXboJ7tL6QqmPKVvwEic +mn43p4DwHyKX8leBBeXkXQKBgFrgOvzOCtmyiEmUNVNzUoLls5Fq66ooSvbmkiWf +ESoLpaxJfQmQI6oZmjCuruadEcZKGy0UKHlntEK6neeW8zi2p3DgWLA2ykF0cggj +QIB/tQy2sDBihxjobt5bhOxrFFtW6Do1wSlJDD/7avt8xM6r2L7qDxGlaPqj5wvO +aVUXAoGAMFAsOK3P5iwahix65VFBkHYf83MLi/k912+1kTSVT149NbMwsek3MubO +qQvfGUfmueHlUl7i0bD8QPgQ3yqrlrVKZGRgT3IQN7sXM5v73UmWyO7Nctpf1U14 +89Z+EEPBSqJP1Ge+EFhNm8iy8NPv7VYuP3YbTtxCwTzq/He3rHw= +-----END RSA PRIVATE KEY----- diff --git a/public.pem b/public.pem new file mode 100644 index 00000000..fbc47dbc --- /dev/null +++ b/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApAJlIIqbo1GPAIuukUzE +waKfQ0jyFxHDos2iPFSgNL6BSW9F0aMyqi80Y98+6YOJSl8ssPIRq6NOCbUngy4v +lUxMr7raw+JXDr0jiQIjogQZpeSMrF4kHMB3f99bd4VOMdYVXk+GPoEwK0ula0rV +RDdqhAWyPOqTibWBEApRtMkT7FgrB0wP0bGP4GS4vBkLX2u+ty7yHfJiZ+KaZyiS +tveFkc0WhWuaUv6pejV8Oqx84Byu0iGk2IpCxDrNkTo8/Z4UVHGrrhAolNIJUd2v +tgJcRLNAhiRMk2oNPcOJWu6l6ZYxYzY+D9Exmw+vb5DKpQC7+aRjb0HsNcDnlyYZ +zQIDAQAB +-----END PUBLIC KEY----- diff --git a/symmetric_key.txt b/symmetric_key.txt new file mode 100644 index 00000000..f129fc12 Binary files /dev/null and b/symmetric_key.txt differ