PK\ $H&׳EEsimple_asym/__init__.pyfrom .asymmetric_encryption import AsymCrypt __all__ = [AsymCrypt] PKK)Hsimple_asym/exceptions.pyclass AsymException(Exception): pass class MissingKeyException(AsymException): pass class MissingAESException(MissingKeyException): message = "Missing AES key. Set or generate one" class MissingRSAPublicException(MissingAESException): message = "Missing public RSA key. Set or generate one to use RSA encryption" class MissingRSAPrivateException(MissingAESException): message = "Missing private RSA key. Set or generate one to use RSA decrypt" PK)H@simple_asym/test.pyimport unittest from .asymmetric_encryption import AsymCrypt from .exceptions import ( MissingAESException, MissingRSAPrivateException, MissingRSAPublicException) class TestAsymCrypt(unittest.TestCase): aes_key = b'uaBbv71UYwAndWfYRGO6lqgkJTylUdqLzCGJ7xLyvq4=' public_key = b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArWrCwDnza3+IRvpCHvKa\nQatyDFFlDrAQvYuZvISkoT+52KOHkCuWbCu/a+mBR1zHS2o75Vvnc0i8T1LWwnQ3\n2xzi4Hhec2i/NLxq72eqmmPY8joSpg6Qpp9CKeGTVt9wLl8ZVnRbI9zAyjY483bk\nCqd/oQvGDC5RVVq7J1gjvyVA6skIH0I5lHYOgsr4cDYUhIt8agN3IuglKZMCySYH\n29C5eWa9trUm6lMsnluu4fWdy14xIIWsG9O7XHtDNmbBTIOExnzCkL7uXaPSthW4\ncoBBV4d5XZ62HTsF6seISuKAQ8VRkY7dwv8K6a4XqJQ5g3/3nNjdjDFo7koCsR7y\nuQIDAQAB\n-----END PUBLIC KEY-----' private_key = b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEArWrCwDnza3+IRvpCHvKaQatyDFFlDrAQvYuZvISkoT+52KOH\nkCuWbCu/a+mBR1zHS2o75Vvnc0i8T1LWwnQ32xzi4Hhec2i/NLxq72eqmmPY8joS\npg6Qpp9CKeGTVt9wLl8ZVnRbI9zAyjY483bkCqd/oQvGDC5RVVq7J1gjvyVA6skI\nH0I5lHYOgsr4cDYUhIt8agN3IuglKZMCySYH29C5eWa9trUm6lMsnluu4fWdy14x\nIIWsG9O7XHtDNmbBTIOExnzCkL7uXaPSthW4coBBV4d5XZ62HTsF6seISuKAQ8VR\nkY7dwv8K6a4XqJQ5g3/3nNjdjDFo7koCsR7yuQIDAQABAoIBAGAZz77y3mBuFjkv\nKkE4NB+8QMFuwc/35e8EU7fS0eDCDd1uEgvk+8EKJVRJ3GiNk6vJPVQHMCYE4sYa\npASyntmAEoJOchkGrR8uYxw0mKhiOLFTWU5IuAR+MQ5AgYQc4m/wJ3xvkqo3BWeJ\n0Nmqwwjcda/rdF7/s/bXBuvwvi6ILv665ZtVwMKtUuY851s0riwUN4cIiifyw/GQ\nLNCrkH2+V3SOMZ8OTOXfMFbDD1cAe4QSPgeFNL/3bKu1e0/Z5b6GLIA9zkiR+0i5\nxq9LDKba/gCyej+YZz9A57MpDi1zmsNV/BPhx9Ns5jY8G9dE1n6/w0Fyf0v+i/93\nhWb1XJkCgYEA0SBF3KgErqsXtz7U05ih0lbKUXnOaJMWewhj0w+zRi9nhJ5X3G2q\nwt+VTyLtoRRW1vklZ9L+zApsfbAROdtA+TA7NFKiu7/de1hzGTWGkqcMEdx3CpLP\nQz7nNchfSAgLagxW6Kb7ob8ytMaegK1id4bOaJ4eV5xemsK3ncf/0OcCgYEA1El9\ntivdU0A5S42qZl4OiD0pHN/E1LOQcwwxKE2Vipzek03NPWtFNOmDRg8mVpbS4MkE\niiH8BnKep4oHY0ac4cBPSQ/7QE2992ge1ru3B8gLcpoeNrJuS6pKr2KPfVQnV+tT\noNtdXoR6EJz9VS12QpbSZF6ClAuOAjgCaIfFi18CgYAoaDr3esOE2Gw5rPtEc055\nLOnkuktmq10BospfAr6aBhjTaCED53DCPJ9F7jLKF/r7iKJwoDU5SZ5S3s1FR5cT\nTv1xi7ID4vuxlJKQwWXiOkK7xMR/l4RSsvnLy46VhXBnKkE0rOccBqyOf34q0NWg\n0LxbPIoSVZV2A7+kzfsg6wKBgA0RIP3PoWX4dA5kf/KhI3/bU+aFF5aIHwIV5Ai5\nDdVkZobmqRV4vt/M59muIQv/aKeReAgQo3S6JW3mnyHLPOjgb4DtzOdeYa0S6aMK\nFvARrjK1rdpsDUH3D3XQOUjbnzhYMeOa3RpuSR0wrJ9LlxXuNrEa6Cq4s1sLm4pX\noR89AoGAOYgo70h6Csg45494yzTsys+gLTytw+wEbefYD4uMLidCgO3hjbuO7G1g\nsOtVPsRVD+8b7qg+45hDMLrcepJeCs751Z6gLCFkJJq2owoSkxxwtDUXgQkZ9NBr\n6dNOEvjztvCkv0n1knFdG1A3VPYHpTI5QIKpA7UxPbdH2p3YkZc=\n-----END RSA PRIVATE KEY-----' def test_bob_alice(self): bob = AsymCrypt() alice = AsymCrypt() bob.make_rsa_keys(bits=2048) alice.make_rsa_keys(bits=2048) bob.make_aes_key() shared_encrypted_aes = bob.get_encrypted_aes_key(alice.public_key) alice.set_aes_key_from_encrypted(shared_encrypted_aes) msg = "hello" msg_ciphertext = bob.encrypt(msg) self.assertNotEqual(msg_ciphertext, msg) decrypted_msg = alice.decrypt(msg_ciphertext).decode() self.assertEqual(decrypted_msg, msg) def test_encrypt_decrypt(self): asym = AsymCrypt(aes_key=self.aes_key, public_key=self.public_key, private_key=self.private_key) msg = "hello" ciphertext = asym.encrypt(msg) self.assertNotEqual(ciphertext, msg) decrypted_msg = asym.decrypt(ciphertext).decode() self.assertEqual(decrypted_msg, msg) def test_passphrase(self): asym = AsymCrypt() private, public, passphrase = asym.make_rsa_keys_with_passphrase() asym = AsymCrypt() asym.set_private_key(private, passphrase=passphrase) self.assertTrue(self.private_key) def test_exceptions(self): asym = AsymCrypt() with self.assertRaises(MissingAESException): asym.encrypt('foo') with self.assertRaises(MissingAESException): asym.decrypt('foo') with self.assertRaises(MissingRSAPublicException): asym.rsa_encrypt('foo') with self.assertRaises(MissingRSAPrivateException): asym.rsa_decrypt('foo') PK1Hɹ  $simple_asym/asymmetric_encryption.pyimport base64 import random import string from cryptography.fernet import Fernet from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.backends import default_backend from .exceptions import ( MissingAESException, MissingRSAPrivateException, MissingRSAPublicException) class AsymCrypt(): aes_cipher = None def __init__(self, aes_key=None, public_key=None, private_key=None): """ A class to encrypt and decrypt using Asymmetrical encryption. All kwargs are optional. :param aes_key: AES key used for symmetric encryption :param public_key: Public RSA key used for asymmetric encryption "param private_key: Private RSA key used for asmmetric decryption """ if aes_key: self.set_aes_key(aes_key) self.set_public_key(public_key) self.set_private_key(private_key) def _get_padding(self): return padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None ) def _random_string(self, N): return ''.join(random.SystemRandom().choice( string.ascii_uppercase + string.digits) for _ in range(N)) def _generate_key(self): return Fernet.generate_key() def _generate_passphrase(self, N=255): return self._random_string(N) def _force_bytes(self, text): try: # Encode if not already done text = text.encode() except AttributeError: pass return text def make_rsa_keys(self, passphrase=None, bits=4096): """ Create new rsa private and public keys :param passphrase: Optional RSA private key passphrase. Returns encrypted version if set :param bits: Bits for pycrypto's generate function. Safe to ignore. :rtype: tuple of string version of keys (private, public) """ self.private_key = rsa.generate_private_key( public_exponent=65537, key_size=bits, backend=default_backend() ) self.public_key = self.private_key.public_key() if passphrase: encryption_alg = serialization.BestAvailableEncryption( passphrase.encode() ) else: encryption_alg = serialization.NoEncryption() private = self.private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=encryption_alg ) public = self.public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) return private, public def make_rsa_keys_with_passphrase(self, bits=4096): """ Wrapper around make_rsa_keys that also generates a passphrase :param bits: Bits for pycrypto's generate function. Safe to ignore. :rtype: tuple (private, public, passphrase) """ passphrase = self._generate_passphrase() private, public = self.make_rsa_keys(passphrase=passphrase, bits=bits) return private, public, passphrase def rsa_encrypt(self, text, use_base64=False): """ Convert plain text to ciphertext :param text: Plaintext to encrypt :param use_base64: set True to return a base64 encoded unicode string (just for convenience) :type use_base64: Boolean :rtype: ciphertext string """ text = self._force_bytes(text) if not self.public_key: raise MissingRSAPublicException ciphertext = self.public_key.encrypt( text, self._get_padding() ) if use_base64 is True: ciphertext = base64.b64encode(ciphertext).decode() return ciphertext def rsa_decrypt(self, ciphertext, use_base64=False): """ Convert ciphertext into plaintext :param ciphertext: Ciphertext to decrypt :param use_base64: set True to return a base64 encoded unicode string (just for convenience) :type use_base64: Boolean :rtype: plaintext string """ if use_base64 is True: ciphertext = base64.b64decode(ciphertext) if not self.private_key: raise MissingRSAPrivateException return self.private_key.decrypt( ciphertext, self._get_padding() ) def set_private_key(self, private_key, passphrase=None): """ Set private key :param private_key: String or RSAPrivateKey object :param passphrase: Optional passphrase for encrpyting the RSA private key :rtype: private key """ if isinstance(private_key, (bytes, str)): private_key = self._force_bytes(private_key) if passphrase: passphrase = self._force_bytes(passphrase) self.private_key = serialization.load_pem_private_key( private_key, password=passphrase, backend=default_backend() ) else: self.private_key = private_key return self.private_key def set_public_key(self, public_key): """ Set public key :param public_key: String or RSAPublicKey object :rtype: public key """ if isinstance(public_key, (bytes, str)): public_key = self._force_bytes(public_key) self.public_key = serialization.load_pem_public_key( public_key, backend=default_backend() ) else: self.public_key = public_key return self.public_key def set_aes_key(self, aes_key): self.aes_key = aes_key self.aes_cipher = Fernet(self.aes_key) def set_aes_key_from_encrypted(self, ciphertext, use_base64=False): """ Set aes_key from an encrypted key A shortcut method for receiving a AES key that was encrypted for our RSA public key :param ciphertext: Encrypted version of the key (bytes or base64 string) :param use_base64: If true, decode the base64 string """ if use_base64 is True: ciphertext = base64.b64decode(ciphertext) aes_key = self.rsa_decrypt(ciphertext) self.set_aes_key(aes_key) def get_encrypted_aes_key(self, public_key, use_base64=False): """ Get encrypted aes_key using specified public_key A shortcut method for sharing a AES key. :param public_key: The public key we want to encrypt for :param use_base64: Will result in the returned key to be base64 encoded :rtype: encrypted key (bytes or base64 string""" public_asym = AsymCrypt(public_key=public_key) encrypted_key = public_asym.rsa_encrypt(self.aes_key) if use_base64 is True: encrypted_key = base64.b64encode(encrypted_key) return encrypted_key def make_aes_key(self): """ Generate a new AES key :rtype: AES key string """ key = self._generate_key() self.set_aes_key(key) return key def encrypt(self, text): """ Encrypt text using AES encryption. Requires public_key and aes_key to be set. aes_key may be generated with AsymCrypt.make_aes_key if you do not already have one. :param text: text to encrypt :rtype: ciphertext string """ text = self._force_bytes(text) if not self.aes_cipher: raise MissingAESException return self.aes_cipher.encrypt(text) def decrypt(self, text): """ Decrypt ciphertext using AES encrpytion. Requires private_key and aes_key to be set. aes_key may have been generated with AsymCrypt.make_aes_key which should have been done at time or encryption. :param text: ciphertext to decrypt :rtype: decrypted text string """ if not self.aes_cipher: raise MissingAESException return self.aes_cipher.decrypt(text) PKמ1H^- /simple_asymmetric-0.2.dist-info/DESCRIPTION.rstUNKNOWN PKמ1H0 A-simple_asymmetric-0.2.dist-info/metadata.json{"classifiers": ["Development Status :: 5 - Production/Stable", "Programming Language :: Python", "Programming Language :: Python :: 3", "Intended Audience :: Developers"], "extensions": {"python.details": {"contacts": [{"email": "david@burkesoftware.com", "name": "David Burke", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/burke-software/simple-asymmetric-python"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["encryption"], "license": "Apache License 2.0", "metadata_version": "2.0", "name": "simple-asymmetric", "run_requires": [{"requires": ["cryptography"]}], "summary": "An easy way to do combined AES and RSA encryption with python", "version": "0.2"}PKמ1HDBE//(simple_asymmetric-0.2.dist-info/pbr.json{"git_version": "d499350", "is_release": false}PKמ1HN -simple_asymmetric-0.2.dist-info/top_level.txtsimple_asym PKמ1H}\\%simple_asymmetric-0.2.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKמ1HJ/++(simple_asymmetric-0.2.dist-info/METADATAMetadata-Version: 2.0 Name: simple-asymmetric Version: 0.2 Summary: An easy way to do combined AES and RSA encryption with python Home-page: https://github.com/burke-software/simple-asymmetric-python Author: David Burke Author-email: david@burkesoftware.com License: Apache License 2.0 Keywords: encryption Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Intended Audience :: Developers Requires-Dist: cryptography UNKNOWN PKמ1HP&simple_asymmetric-0.2.dist-info/RECORDsimple_asym/__init__.py,sha256=c9WFg_U9uzY527H4lVqEIYw5IGmdrtl5wpoUNB7E0hs,69 simple_asym/asymmetric_encryption.py,sha256=A8UPqwdA_vsAQExegOiXtveA9gw3asO1gtYGPna2i_U,8206 simple_asym/exceptions.py,sha256=BP7Im7W1tgdzsTftAX0f59-tYMNBLQ19lTkjQFdzEy8,472 simple_asym/test.py,sha256=SKvAr8eZDjgg5v4jCJFMv6BiCcroO7wgji6KEf4MSos,4111 simple_asymmetric-0.2.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 simple_asymmetric-0.2.dist-info/METADATA,sha256=QA5AFomXTz3RjGydL7J5QE9dfyYLvDQNcQtbIrB-xGs,555 simple_asymmetric-0.2.dist-info/RECORD,, simple_asymmetric-0.2.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 simple_asymmetric-0.2.dist-info/metadata.json,sha256=pJCDVoo-2v0WB4DyNO0IglwZJWlF5OquDXzI6kwqsLE,755 simple_asymmetric-0.2.dist-info/pbr.json,sha256=VKxZa4lmmxNCe-aNYlBaj58UL60NE5I4crHS_RrLeUY,47 simple_asymmetric-0.2.dist-info/top_level.txt,sha256=P5OwbwQKKXKcrgF5-JBkP2uOBLvbfuSSWgL6zZ-aJXE,12 PK\ $H&׳EEsimple_asym/__init__.pyPKK)Hzsimple_asym/exceptions.pyPK)H@simple_asym/test.pyPK1Hɹ  $simple_asym/asymmetric_encryption.pyPKמ1H^- /3simple_asymmetric-0.2.dist-info/DESCRIPTION.rstPKמ1H0 A-p3simple_asymmetric-0.2.dist-info/metadata.jsonPKמ1HDBE//(6simple_asymmetric-0.2.dist-info/pbr.jsonPKמ1HN -#7simple_asymmetric-0.2.dist-info/top_level.txtPKמ1H}\\%z7simple_asymmetric-0.2.dist-info/WHEELPKמ1HJ/++(8simple_asymmetric-0.2.dist-info/METADATAPKמ1HP&:simple_asymmetric-0.2.dist-info/RECORDPK >