13

I'm trying to encrypt and decrypt text in python, and I know how to do that - the problem is that I don't want to have to use a set amount of letters like 16 or 32. I want to be able to use as many letters/numbers as I want, and then encrypt the text without any errors.

base64 in python would be perfect, because I can do just that, but when I want to do:

password = "password"
encode = base64.b64encode(password)

... it returns an error because it's not in bytes; it has to be like:

encode = base64.b64encode(b'password')

That works completely fine, but I don't want to do that.

import base64

password = "hello world"  
encoded = base64.b64encode(password.encode("utf-8"))
print(encoded)
decoded = base64.b64decode(encoded)
print(decoded)

that is now my code and it works fine but i now know i was using the wrong type of thing i need to know on how to use AES.

georg
  • 211,518
  • 52
  • 313
  • 390
user2387537
  • 361
  • 3
  • 9
  • 17

1 Answers1

33

In Python 3, you need to convert your string to bytes, since base64 encoding depends on the encoding of the string, and Python 3 doesn't make assumptions about string encoding. See this question.

import base64

# Assuming UTF-8 encoding, change to something else if you need to
base64.b64encode("password".encode("utf-8"))

This page explains why strings act differently in Python 3:

The biggest difference with the 2.x situation is that any attempt to mix text and data in Python 3.0 raises TypeError, whereas if you were to mix Unicode and 8-bit strings in Python 2.x, it would work if the 8-bit string happened to contain only 7-bit (ASCII) bytes, but you would get UnicodeDecodeError if it contained non-ASCII values. This value-specific behavior has caused numerous sad faces over the years.

And, like sberry said, base64 encoding is not encryption. If you actually want this to be secure, you'll need to use something like AES, or if you just want to safely store the password, use bcrypt or PBKDF2.


Here's an example of using PyCrypto to encrypt something with AES, using a key derived from a password using PBKDF2.

#!/usr/bin/env python3

from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Protocol.KDF import PBKDF2

def make_key(password, salt = None):
    if salt is None:
        # Generate a key from the password
        salt = Random.new().read(8)

    # You probably want to adjust the number of iterations
    # based on your target platform and willingness to wait.
    # Somewhere around 10,000 will give you reasonable security.
    # If you don't mind the wait, 100,000 is better.
    # If you have a really fast computer, or are willing to wait a long
    # time, feel free to set it even higher.
    key = PBKDF2(password, salt, AES.block_size, 100000)
    return (key, salt)

def encrypt(message, key):
    # The IV should always be random
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CFB, iv)
    ciphertext = cipher.encrypt(message.encode("utf-8"))
    return (ciphertext, iv)

def decrypt(ciphertext, key, iv):
    cipher = AES.new(key, AES.MODE_CFB, iv)
    msg = cipher.decrypt(ciphertext).decode("utf-8")
    return msg

def main():
    # Encryption
    password = "correct horse battery staple"
    message = "Super secret information that shouldn't be seen by attackers"
    key, salt = make_key(password)
    ciphertext, iv = encrypt(message, key)
    print(b"The ciphertext is: " + ciphertext)

    # Decryption

    # In normal cases, you now need to store the salt and iv somewhere
    # Usually you prepend them to the ciphertext
    # I don't feel like doing that, so we'll just assume that I got the salt
    # and IV somehow.
    key, _ = make_key(password, salt)
    cleartext = decrypt(ciphertext, key, iv)
    print("The cleartext is: " + cleartext)

if __name__ == "__main__":
    main()

Just using AES like this provides confidentiality (an attacker can't read the message without the password), but not integrity (an attacker could insert data into the ciphertext, and the only way you could notice is that it would probably decrypt as garbage). To prevent that, you can also use a message authentication code to ensure that the ciphertext hasn't been changed by someone who doesn't have the password.


I thought this was an interesting exercise, so I put a more complete example in a BitBucket repo. It adds an HMAC, and reads and writes from a JSON file.

Community
  • 1
  • 1
Brendan Long
  • 53,280
  • 21
  • 146
  • 188
  • Thank you @BrendanLong ! i understand how it works now but do you know if it is possible to be able to encrypt text without having to worry about how many letter/numbers you need to use for it to work correctly? – user2387537 May 24 '13 at 17:24
  • @user2387537 Do you mean how AES requires the key to be a particular length? The standard way of handling that is to use a key derivation function like [PBKDF2](https://www.dlitz.net/software/pycrypto/api/current/Crypto.Protocol.KDF-module.html#PBKDF2) (this documentation is for [PyCrypto](https://www.dlitz.net/software/pycrypto/)). You give it some information (password, salt, number of iterations, desired key length), and it spits out a key. PBKDF2 is also one of the more secure ways to hash a password (along with [bcrypt](http://bcrypt.sourceforge.net/)). – Brendan Long May 24 '13 at 17:30
  • yeah that's what i mean, i'm pretty new to coding, i know most of the basics but now i'm starting to make applications and that's why i need to know encryption but i have tried loads of times and each time i need a key to be a particular length like you said but i don't want to have a length i want to be able to have my text as long/short as i want and then i want the text to be encrypted @BrendanLong – user2387537 May 24 '13 at 17:34
  • @user2387537 With password hashing, you don't want to use reversible encryption (since an attacker could just reverse the encryption and get the password). Having a fixed output length helps hide the password better. Hash functions shouldn't care how long the input is though. If you want to encrypt something with a password, you should use PBKDF2 to generate a key with the correct length (it will take input of any length and give you a key of the length you tell it to), then use that with [AES](https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.AES-module.html). – Brendan Long May 24 '13 at 17:37
  • @user2387537 I added an example of how to use PyCrypto to do this. – Brendan Long May 24 '13 at 18:11
  • And a BitBucket repo if you want a complete version that does an HMAC and reads and writes files. – Brendan Long May 24 '13 at 20:03
  • Thank you so much! @BrendanLong you have destroyed my question! people like you should be thanked beyond thanked! For people like me who have just started coding pretty much and have no idea where to look about coding you are pretty much a god, thank you again – user2387537 May 24 '13 at 20:25
  • Is there any way to do with using SIX module in python. As it will help in switching between python3 and python2. – Aman Gupta Jun 21 '20 at 06:30
  • Thank for save 3 days issue debug – Thoman Mar 06 '22 at 16:33