76

I have been looking for sometime on how to encrypt and decrypt a string. But most of it is in 2.7 and anything that is using 3.2 is not letting me print it or add it to a string.

So what I'm trying to do is the following:

mystring = "Hello stackoverflow!"
encoded = encode(mystring,"password")
print(encoded)

jgAKLJK34t3g (a bunch of random letters)

decoded = decode(encoded,"password")
print(decoded)

Hello stackoverflow!

Is there anyway of doing this, using python 3.X and when the string is encoded it's still a string, not any other variable type.

David
  • 923
  • 1
  • 9
  • 11
  • It depends if you are looking for extreme security or you just whish the string not to be readable at first glance. – servabat Dec 06 '14 at 19:49
  • I don't really need it to secury, but the more secure the better! I'd also want to keep the encoded string short aswell. – David Dec 06 '14 at 19:51
  • If you are looking for some solid library you can use pycrypto for example – servabat Dec 06 '14 at 19:54
  • 2
    Note that what you're doing is encoding and has very little to do with actual encryption. A cipher takes a key and produces bytes. Those are not printable, but you can encode them in Hex, Base32, Base64, etc. – Artjom B. Dec 07 '14 at 10:32
  • There are interface for GNUPG like pypgpme – somenxavier Dec 10 '17 at 19:01

9 Answers9

78

I had troubles compiling all the most commonly mentioned cryptography libraries on my Windows 7 system and for Python 3.5.

This is the solution that finally worked for me.

from cryptography.fernet import Fernet
key = Fernet.generate_key() #this is your "password"
cipher_suite = Fernet(key)
encoded_text = cipher_suite.encrypt(b"Hello stackoverflow!")
decoded_text = cipher_suite.decrypt(encoded_text)
KRBA
  • 929
  • 6
  • 9
41

Take a look at PyCrypto. It supports Python 3.2 and does exactly what you want.

From their pip website:

>>> from Crypto.Cipher import AES
>>> obj = AES.new('This is a key123', AES.MODE_CFB, 'This is an IV456')
>>> message = "The answer is no"
>>> ciphertext = obj.encrypt(message)
>>> ciphertext
'\xd6\x83\x8dd!VT\x92\xaa`A\x05\xe0\x9b\x8b\xf1'
>>> obj2 = AES.new('This is a key123', AES.MODE_CFB, 'This is an IV456')
>>> obj2.decrypt(ciphertext)
'The answer is no'

If you want to encrypt a message of an arbitrary size use AES.MODE_CFB instead of AES.MODE_CBC.

Piotr Dabkowski
  • 5,661
  • 5
  • 38
  • 47
  • When I try import it it gives me an error: >>> from Crypto.Cipher import AES Traceback (most recent call last): File "", line 1, in from Crypto.Cipher import AES File "C:\Python34\lib\Crypto\Cipher\AES.py", line 50, in from Crypto.Cipher import _AES ImportError: cannot import name '_AES' >>> Did I install it right? I downloaded Crypto and copied the folder which is in the lib folder into the Lib folder in pythons directory. – David Dec 07 '14 at 14:28
  • 2
    Copying sometimes is not enough. Use python setup.py install or type pip install pycrypto in your command line. You can download pip here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pip – Piotr Dabkowski Dec 07 '14 at 15:55
  • 3
    In case u need to encrypt a message of arbitrary size other than size n*16 byte, u can use AES.MODE_CFB instead of MODE_CBC. obj = AES.new('This is a key123', AES.MODE_CFB, 'This is an IV456'). Refer the docs at http://pythonhosted.org/pycrypto/ – SanD Dec 15 '15 at 23:39
  • I'm getting this error while encoding `'utf-8' codec can't decode byte 0xa6 in position 7: invalid start byte` – Rohit Khatri Nov 17 '16 at 10:16
  • Error: Input strings must be a multiple of 16 in length. – ADL Apr 26 '17 at 05:54
  • @RohitKhatri Did you get the solution to your error ? – Ankur Sharma Oct 16 '17 at 12:13
16

Try this:

Python Cryptography Toolkit (pycrypto) is required

$ pip install pycrypto

pycrypto package is outdated and has not been maintained since 2014. There is a drop-in replacement package called pycryptodome.

$ pip install pycryptodome

And the code below flawlessly works on python 3.8

Code:

from Crypto.Cipher import AES
from base64 import b64encode, b64decode


class Crypt:

    def __init__(self, salt='SlTKeYOpHygTYkP3'):
        self.salt = salt.encode('utf8')
        self.enc_dec_method = 'utf-8'

    def encrypt(self, str_to_enc, str_key):
        try:
            aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, self.salt)
            hx_enc = aes_obj.encrypt(str_to_enc.encode('utf8'))
            mret = b64encode(hx_enc).decode(self.enc_dec_method)
            return mret
        except ValueError as value_error:
            if value_error.args[0] == 'IV must be 16 bytes long':
                raise ValueError('Encryption Error: SALT must be 16 characters long')
            elif value_error.args[0] == 'AES key must be either 16, 24, or 32 bytes long':
                raise ValueError('Encryption Error: Encryption key must be either 16, 24, or 32 characters long')
            else:
                raise ValueError(value_error)

    def decrypt(self, enc_str, str_key):
        try:
            aes_obj = AES.new(str_key.encode('utf8'), AES.MODE_CFB, self.salt)
            str_tmp = b64decode(enc_str.encode(self.enc_dec_method))
            str_dec = aes_obj.decrypt(str_tmp)
            mret = str_dec.decode(self.enc_dec_method)
            return mret
        except ValueError as value_error:
            if value_error.args[0] == 'IV must be 16 bytes long':
                raise ValueError('Decryption Error: SALT must be 16 characters long')
            elif value_error.args[0] == 'AES key must be either 16, 24, or 32 bytes long':
                raise ValueError('Decryption Error: Encryption key must be either 16, 24, or 32 characters long')
            else:
                raise ValueError(value_error)

Usage:

        test_crpt = Crypt()
        test_text = """Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""

        test_key = 'MyKey4TestingYnP'
        test_enc_text = test_crpt.encrypt(test_text, test_key)
        test_dec_text = test_crpt.decrypt(test_enc_text, test_key)
        print(f'Encrypted:{test_enc_text}  Decrypted:{test_dec_text}')
Cagatay Barin
  • 3,428
  • 2
  • 24
  • 43
Raoof Arakkal
  • 161
  • 1
  • 5
  • i have just copy pasted your code and did nothing more. my error is: `TypeError: Object type cannot be passed to C code` – Naveen Reddy Marthala Dec 30 '19 at 11:34
  • 1
    If you're using python 3, use the pycryptodome package with the same code in this answer. It works for me on python 3.8 , so I've modified the answer (including a minor bugfix in the code) – Cagatay Barin Nov 10 '21 at 08:34
13

You can do this easily by using the library cryptocode. Here is how you install:

pip install cryptocode

Encrypting a message (example code):

import cryptocode

encoded = cryptocode.encrypt("mystring","mypassword")
## And then to decode it:
decoded = cryptocode.decrypt(encoded, "mypassword")

Documentation can be found here

Scott
  • 4,974
  • 6
  • 35
  • 62
gdavid7
  • 241
  • 4
  • 5
13

Encrypt Data

First, we need to install the cryptography library:

pip3 install cryptography
  • From the cryptography library, we need to import Fernet and start generating a key - this key is required for symmetric encryption/decryption.

  • To generate a key, we call the generate_key() method.

    • We only need to execute the above method once to generate a key.

    You need to keep this key in a safe place. If you lose the key, you won't be able to decrypt the data that was encrypted with this key.

  • Once we have generated a key, we need to load the key with load_key()

Encrypt a Message

This is a three step process:

  1. encode the message
  2. initialize the Fernet class
  3. pass the encoded message to encrypt() method

Below is a full working example of encrypting a message :

from cryptography.fernet import Fernet

def generate_key():
    """
    Generates a key and save it into a file
    """
    key = Fernet.generate_key()
    with open("secret.key", "wb") as key_file:
        key_file.write(key)

def load_key():
    """
    Load the previously generated key
    """
    return open("secret.key", "rb").read()

def encrypt_message(message):
    """
    Encrypts a message
    """
    key = load_key()
    encoded_message = message.encode()
    f = Fernet(key)
    encrypted_message = f.encrypt(encoded_message)

    print(encrypted_message)

if __name__ == "__main__":
    # generate_key() # execute only once 
    encrypt_message("Hello stackoverflow!")

output:

b'gAAAAABgLX7Zj-kn-We2BI_c9NQhEtfJEnHUVhVqtiqjkDi5dgJafj-_8QUDyeNS2zsJTdBWg6SntRJOjOM1U5mIxxsGny7IEGqpVVdHwheTnwzSBlgpb80='

Decrypt Data

To decrypt the message, we just call the decrypt() method from the Fernet library. Remember, we also need to load the key as well, because the key is needed to decrypt the message.

from cryptography.fernet import Fernet

def load_key():
    """
    Load the previously generated key
    """
    return open("secret.key", "rb").read()

def decrypt_message(encrypted_message):
    """
    Decrypts an encrypted message
    """
    key = load_key()
    f = Fernet(key)
    decrypted_message = f.decrypt(encrypted_message)

    print(decrypted_message.decode())

if __name__ == "__main__":
    decrypt_message(b'gAAAAABgLX7Zj-kn-We2BI_c9NQhEtfJEnHUVhVqtiqjkDi5dgJafj-_8QUDyeNS2zsJTdBWg6SntRJOjOM1U5mIxxsGny7IEGqpVVdHwheTnwzSBlgpb80=')

output:

Hello stackoverflow!


Your password is in the secret.key in a form similar to the password below:

B8wtXqwBA_zb2Iaz5pW8CIQIwGSYSFoBiLsVz-vTqzw=
Milovan Tomašević
  • 6,823
  • 1
  • 50
  • 42
  • Can you tell me how can I use it with Django.? **What I have tried:** I'm storing the encrypted message on the database. after that, I'm trying to decrypt that message and getting `token must be bytes` error! – Sanjay Sikdar Sep 07 '21 at 19:25
  • I think that in the first step you should have: `cript_body_message = decipher.decrypt(form.message.data).decode('utf-8')` this will encrypt the message and prepare it for storage. After that, you probably create an object with `message = Message(header=form.header.data, body=cript_body_message)`. And finally adding to the database `db.session.add(message)` and `db.session.commit()`. Something personal to him should do. It should be noted that when reading, it is necessary to decipher the message. I hope I helped at least a little. – Milovan Tomašević Sep 07 '21 at 19:43
  • 1
    You should also look at: [django-crypto-fields](https://github.com/erikvw/django-crypto-fields), [django-cryptographic-fields](https://github.com/foundertherapy/django-cryptographic-fields/), [django-cryptography](https://github.com/georgemarshall/django-cryptography) ... The documentations is excellent as are the examples. Happy coding (: – Milovan Tomašević Sep 07 '21 at 19:54
3

Although its very old, but I thought of sharing another idea to do this:

from Crypto.Cipher import AES    
from Crypto.Hash import SHA256

password = ("anything")    
hash_obj = SHA256.new(password.encode('utf-8'))    
hkey = hash_obj.digest()

def encrypt(info):
    msg = info
    BLOCK_SIZE = 16
    PAD = "{"
    padding = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PAD
    cipher = AES.new(hkey, AES.MODE_ECB)
    result = cipher.encrypt(padding(msg).encode('utf-8'))
    return result  

msg = "Hello stackoverflow!"
cipher_text = encrypt(msg)
print(cipher_text)

def decrypt(info):
    msg = info
    PAD = "{"
    decipher = AES.new(hkey, AES.MODE_ECB)
    pt = decipher.decrypt(msg).decode('utf-8')
    pad_index = pt.find(PAD)
    result = pt[: pad_index]
    return result

plaintext = decrypt(cipher_text)
print(plaintext)

Outputs:

> b'\xcb\x0b\x8c\xdc#\n\xdd\x80\xa6|\xacu\x1dEg;\x8e\xa2\xaf\x80\xea\x95\x80\x02\x13\x1aem\xcb\xf40\xdb'

> Hello stackoverflow!
Tejj
  • 161
  • 1
  • 7
1

You may use Fernet as follows:

from cryptography.fernet import Fernet
key = Fernet.generate_key()
f = Fernet(key)
encrypt_value = f.encrypt(b"YourString")
f.decrypt(encrypt_value)
-1

Here's a simple algorithm akin to a one time pad...for illustrative purposes how you might use one str to encrypt another and then decrypt the cipher text.

def explact(secret: str, key: str) -> str:
    """use one secret (key) to encrypt another (secret)"""
    explacted_secret_arr = []
    for idx, c in enumerate(secret):
        explacted_secret_arr.append(chr(ord(c) + ord(key[idx % len(key)])))

    return "".join(explacted_secret_arr)


def unexplact(explacted_secret: str, key: str) -> str:
    """use one secret (key) to decrypt another (explacted_secret)"""
    secret_arr = []
    for idx, c in enumerate(explacted_secret):
        secret_arr.append(chr(ord(c) - ord(key[idx % len(key)])))

    return "".join(secret_arr)

assert unexplact(explact("secret", "key"), "key") == "secret"
jsnow
  • 1,399
  • 1
  • 9
  • 7
-4

For Encryption

  def encrypt(my_key=KEY, my_iv=IV, my_plain_text=PLAIN_TEXT): 

       key = binascii.unhexlify('ce975de9294067470d1684442555767fcb007c5a3b89927714e449c3f66cb2a4')
       iv = binascii.unhexlify('9aaecfcf7e82abb8118d8e567d42ee86')

       padder = PKCS7Padder()
       padded_text = padder.encode(my_plain_text)

      encryptor = AES.new(key, AES.MODE_CBC, iv, segment_size=128)  # Initialize encryptor
      result = encryptor.encrypt(padded_text)  

     return {
         "plain": my_plain_text,
         "key": binascii.hexlify(key),
         "iv": binascii.hexlify(iv),
         "ciphertext": result
}

For Decryption:

     def decrypt(my_key=KEY, my_iv=IV, encryptText=encrypttext):

        key = binascii.unhexlify(my_key)
        iv = binascii.unhexlify(my_iv)
        encryptor = AES.new(key, AES.MODE_CBC, iv, segment_size=128)  # Initialize encryptor
        result = encryptor.decrypt(binascii.a2b_hex(encryptText))
        padder = PKCS7Padder()
        decryptText=padder.decode(result)  

           return {
          "plain": encryptText,
          "key": binascii.hexlify(key),
          "iv": binascii.hexlify(iv),
          "decryptedTest": decryptText
}
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140