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
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.