How to encrypt messages with ECC in pycryptodom - python

I'm using Hybrid encryption(RSA+AES) but the length was large and now i want to use ECC instead of RSA, but in pycryptodom there is no implementation for that..
this is my RSA code
def generate_keys():
key = RSA.generate(1024)
private_key = key.exportKey(format='PEM', pkcs=8,
protection="scryptAndAES128-CBC")
f = open("private_key.pem", "wb")
f.write(private_key)
public_key = key.publickey().exportKey('PEM')
f = open("public_key.pem", "wb")
f.write(public_key)
f.close()
def encrypt(username, msg):
#get the reciever's public key
f = open("{}.pem".format(username)) # a.salama.pem
recipient_key = RSA.import_key(f.read())
f.close()
# Encrypt the session key with the reciever's public RSA key
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# Encrypt the data with the AES session key
session_key = get_random_bytes(16)
cipher_aes = AES.new(session_key, AES.MODE_EAX)
ciphertext, tag = cipher_aes.encrypt_and_digest(msg.encode('utf-
8'))
encrypted_data = cipher_rsa.encrypt(session_key) +
cipher_aes.nonce + tag + ciphertext
encrypted_data = base64.b64encode(encrypted_data)
return encrypted_data
And after trying to use ECC+AES the code will be
from Crypto.PublicKey import ECC
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
import base64
def generate_keys():
key = ECC.generate(curve='P-256') #3072 RSA
private_key = key.export_key(format='PEM')
f = open('private_key.pem','wt')
f.write(private_key)
f.close()
public_key = key.public_key().export_key(format='PEM')
f = open('public_key.pem','wt')
f.write(public_key)
f.close()
def encrypt(username, msg):
#get the reciever's public key
f = open("{}.pem".format(username), 'rt') # a.salama.pem
recipient_key = ECC.import_key(f.read())
f.close()
# Encrypt the session key with the reciever's public RSA key
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# Encrypt the data with the AES session key
session_key = get_random_bytes(16)
#we use the EAX mode to allow detection of unauthorized
modifications.
cipher_aes = AES.new(session_key, AES.MODE_EAX)
ciphertext, tag = cipher_aes.encrypt_and_digest(msg.encode('utf-
8'))
encrypted_data = cipher_rsa.encrypt(session_key) +
cipher_aes.nonce + tag + ciphertext
encrypted_data = base64.b64encode(encrypted_data)
return encrypted_data.decode()
This gives me error in this line
cipher_rsa = PKCS1_OAEP.new(recipient_key)
but i want to encrypt the session key with the public key, how to do this with pycryptodome or any other way

Pycryptodome does not support elliptic curve-based encryption (ECC encryption).
Use the ECIES algorithm instead, e.g. this Python library: https://github.com/kigawas/eciespy
The ECIES (Elliptic Curve Integrated Encryption Scheme) is hybrid encryption scheme, which combines ECC public-key cryptography to asymmetrically encrypt a session key, used later to encrypt the input data with a symmetric cipher (e.g. with AES-GCM).

I know this is an old question, but to anyone else that comes here:
You can use Pycryptodome or Cryptography for this now. Using Pycrptodome for example:
from Crypto.PublicKey import ECC
def get_or_create_public_key(filename: str = "private_key.pem"):
""" Helper function to retrieve public key """
private_key_file = os.path.join(settings.BASE_DIR, filename)
if os.path.exists(private_key_file):
file = open(private_key_file, "rt")
private_key = ECC.import_key(file.read(), passphrase=settings.SECRET_KEY)
else:
private_key = ECC.generate(curve="P-256")
file = open(private_key_file, "wt")
file.write(
private_key.export_key(
format="PEM",
use_pkcs8=True,
passphrase=settings.SECRET_KEY,
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC",
)
)
file.close()
public_key = private_key.public_key()
return public_key.export_key(format="PEM")

Related

Amazon selling-partner-api feed encrypt file with Python

I tried GreatFeedDocument, then I received a status code is 200, but the get feed result's status:
"processingStatus": "FATAL"
I have too many times tried but I can't understand,
How I can encrypt the XML file?
Here is my python script.
from aws_auth import Auth
import requests
import json
from pprint import pprint
import base64, os
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
ownercd = "****"
sp_auth = Auth(ownercd)
def pad(s):
# Data will be padded to 16 byte boundary in CBC mode
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def getKey(password):
# Use SHA256 to hash password for encrypting AES
hasher = SHA256.new(password.encode())
return hasher.digest()
# Encrypt message with password
def encrypt(message, key, iv, key_size=256):
message = pad(message)
cipher = AES.new(key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(message)
# Encrypt file
def encrypt_file(file_name, key, iv):
# Open file to get file Data
with open(file_name, "rb") as fo:
plaintext = fo.read()
# Encrypt plaintext with key has been hash by SHA256.
enc = encrypt(plaintext, key, iv)
# write Encrypted file
with open(file_name + ".enc", "wb") as fo:
fo.write(enc)
return enc
if sp_auth != False:
x_amz_access_token = sp_auth[0] # AWS SP-api access token
AWSRequestsAuth = sp_auth[1] # AWS signature
config = sp_auth[2] # From mongo's config
feed_headers = {
"Content-Type": "application/json",
"x-amz-access-token": x_amz_access_token,
}
contentType = {"contentType": "application/xml; charset=UTF-8"}
# [1.1] Create a FeedDocument
creat_feed_res = requests.post(
config["BASE_URL"] + "/feeds/2020-09-04/documents",
headers=feed_headers,
auth=AWSRequestsAuth,
data=json.dumps(contentType),
)
# [1.2] Store the response
CreatFeedResponse = creat_feed_res.json()["payload"]
feedDocumentId = CreatFeedResponse["feedDocumentId"]
initializationVector = CreatFeedResponse["encryptionDetails"][ "initializationVector"]
url = CreatFeedResponse["url"]
key = CreatFeedResponse["encryptionDetails"]["key"]
# [1.3] Upload and encrypt document
filename = "carton.xml"
iv = base64.b64decode(initializationVector)
encrypt_data = encrypt_file(filename, getKey(key), iv)
headers = {"Content-Type": "application/xml; charset=UTF-8"}
res = requests.put(url, headers=headers, data=encrypt_data)
print(res.status_code) # 200
print(res.request.body) # b'8L^\xbeY\xf....
print(res.content.decode())
It is the GetFeed Response:
Can anyone help me with that? Thanks in advance!
I solved my problem.
Here is my example code.
import requests
import json
from pprint import pprint
import base64, os
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from base64 import b64encode
import pyaes
dirname = os.path.dirname(os.path.abspath(__file__))
xmlFile_Name = dirname + "/template/carton.xml"
def encrypt(key, initializationVector):
# Create random 16 bytes IV, Create 32 bytes key
key = base64.b64decode(key)
iv = base64.b64decode(initializationVector)
# Encryption with AES-256-CBC
if os.path.isfile(xmlFile_Name) == False:
print("===== CARTON FILE NOT FOUND FROM ===== : {}".format(xmlFile_Name))
sys.exit()
with open(xmlFile_Name, "r") as fo:
plaintext = fo.read()
# print(plaintext)
encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
ciphertext = encrypter.feed(plaintext.encode("utf8"))
ciphertext += encrypter.feed()
res = requests.put(
url,
data=ciphertext,
headers=contentType,
)
print("STATUS UPLOAD: ", res.status_code) # 200
if res.status_code == 200:
print("===== FILE UPLOAD DONE =====")
# REQUEST
feed_headers = {
"Content-Type": "application/json",
"x-amz-access-token": x_amz_access_token,
}
contentType = {"contentType": "application/xml; charset=UTF-8"}
creat_feed_res = requests.post(
config["BASE_URL"] + "/feeds/2020-09-04/documents",
headers=feed_headers,
auth=AWSRequestsAuth,
data=json.dumps(contentType),
)
# [1.2] Store the response
CreatFeedResponse = creat_feed_res.json()["payload"]
feedDocumentId = CreatFeedResponse["feedDocumentId"]
initializationVector = CreatFeedResponse["encryptionDetails"][ "initializationVector"]
url = CreatFeedResponse["url"]
key = CreatFeedResponse["encryptionDetails"]["key"]
# print(feedDocumentId)
# print("KEY =>", key)
# print("IV =>", initializationVector)
# print(url) #s3 url
contentType = {"Content-Type": "application/xml; charset=UTF-8"}
encryptFile = encrypt(key, initializationVector)
# [3.1] Create a feed
feedType = "POST_FBA_INBOUND_CARTON_CONTENTS"
marketplaceIds = "A1VC38T7*****"
inputFeedDocumentId = feedDocumentId
createdFeedURL = "https://sellingpartnerapi-fe.amazon.com/feeds/2020-09-04/feeds"
createFeedBody = {
"inputFeedDocumentId": inputFeedDocumentId,
"feedType": feedType,
"marketplaceIds": [marketplaceIds],
}
resCreatFeed = requests.post(
createdFeedURL,
headers=feed_headers,
auth=AWSRequestsAuth,
data=json.dumps(createFeedBody),
)
createFeedStatusCode = resCreatFeed.json()
if resCreatFeed.status_code == 202:
# print("Steph 2).")
feedId = createFeedStatusCode["payload"]["feedId"]
print("FEED ID: ", feedId)
# [3.2] CET a feed
getFeed = requests.get(
config["BASE_URL"] +
"/feeds/2020-09-04/feeds/{}".format(str(feedId)),
headers=feed_headers,
auth=AWSRequestsAuth,
)
pprint(getFeed.json()['payload'])
Here is my Get Feed response:
{'payload': {
'createdTime': '2021-02-24T07:43:22+00:00',
'feedId': '14795****',
'feedType': 'POST_FBA_INBOUND_CARTON_CONTENTS',
'marketplaceIds': ['A1VC38T7****'],
'processingStatus': 'DONE'}
}
Then I used the cartonID, I could download the get labels PDF file from the URL.
PackageLabelsToPrint is your cartonID you must set the CartonID here, for example:
Last, I could download a PDF file using the URL
Here is my GetLabel:

Python RSA message encryption "Plaintext is too long"?

I'm using the following code to implement a basic RSA solution for encrypting and decrypting data, but when I try to encrypt any text that is longer than 87 characters I get an error saying Plaintext is too long from the file Crypto/Cipher/PKCS1_OAEP.py.
I read on other questions that RSA cannot encrypt and decrypt large sets of data but I'm not sure if that's true. If it is, what other options can I use to encrypt any amount of data?
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode
hash = "SHA-256"
def newkeys(keysize):
random_generator = Random.new().read
key = RSA.generate(keysize, random_generator)
private, public = key, key.publickey()
return public, private
def encrypt(message, pub_key):
cipher = PKCS1_OAEP.new(pub_key)
return cipher.encrypt(message)
i fixed it like this : [This is on my code i havent edited yours]
with open(file, 'rb') as f:
fernet_key = f.read()
if len(fernet_key) > 80 :
fernet_key1 = ''
tostart = 0
toadd = 0
flag_tostart = 0
while True:
if fernet_key1 == fernet_key:
break
else:
flag_tostart = flag_tostart +1
if flag_tostart == '2'or flag_tostart > 2 or flag_tostart == 2:
tostart = tostart + 80
toadd = toadd +80
if tostart == 0 :
tostart = 0
else:
tostart = tostart + 80
fernet_key[tostart:toadd] += fernet_key1
public_crypter = PKCS1_OAEP.new(public_key)
with open(file, 'wb') as f:
enc_fernet_key =
public_crypter.encrypt(fernet_key1)
f.write(enc_fernet_key)

Python: Cryptodomex not working with DSA verification

I tried to verify the signature of a file by DSA encryption. I am using Pyton 3.6 and pycryptodomex version 3.4.7.
Unhappily the documentation code seems to be outdated (was trying to get a simple example working):
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
# Create a new DSA key
key = DSA.generate(2048)
f = open("public_key.pem", "w")
f.write(key.publickey().exportKey(key))
# Sign a message
message = b"Hello"
hash_obj = SHA256.new(message)
signer = DSS.new(key, 'fips-186-3')
signature = key.sign(hash_obj)
# Load the public key
f = open("public_key.pem", "r")
hash_obj = SHA256.new(message)
pub_key = DSA.import_key(f.read())
# Verify the authenticity of the message
if pub_key.verify(hash_obj, signature):
print "OK"
else:
print "Incorrect signature"
this is my code, tried to fix the function calls which were not working:
from Cryptodome.PublicKey import DSA
from Cryptodome.Signature import DSS
from Cryptodome.Hash import SHA256
# Create a new DSA key
key = DSA.generate(2048)
print(key.exportKey())
# Sign a message
message = b"Hello"
hash_obj = SHA256.new(message)
signer = DSS.new(key, 'fips-186-3')
signature = signer.sign(hash_obj)
# Load the public key
pub_key = DSA.import_key(key.publickey().exportKey())
verifyer = DSS.new(pub_key, 'fips-186-3')
hash_obj = SHA256.new(message)
# Verify the authenticity of the message
if verifyer.verify(hash_obj, signature):
print("OK")
else:
print("Incorrect signature")
Can someone help me with this topic?
works like that
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
import base64
# Create a new DSA key
key = DSA.generate(2048)
f = open("public_key.pem", "w")
f.write(base64.b64encode(key.publickey().export_key()).decode("utf-8") )
f.close()
# Sign a message
message = b"Hello"
hash_obj = SHA256.new(message)
signer = DSS.new(key, 'fips-186-3')
signature = signer.sign(hash_obj)
# Load the public key
f = open("public_key.pem", "r")
hash_obj = SHA256.new(message)
pub_key = DSA.import_key(base64.b64decode(f.read()))
verifier = DSS.new(pub_key, 'fips-186-3')
# Verify the authenticity of the message
try:
verifier.verify(hash_obj, signature)
print("The message is authentic.")
except ValueError:
print("The message is not authentic.")

Python transfer encrypted image using CRYPTO AES [duplicate]

OpenSSL provides a popular (but insecure – see below!) command line interface for AES encryption:
openssl aes-256-cbc -salt -in filename -out filename.enc
Python has support for AES in the shape of the PyCrypto package, but it only provides the tools. How to use Python/PyCrypto to decrypt files that have been encrypted using OpenSSL?
Notice
This question used to also concern encryption in Python using the same scheme. I have since removed that part to discourage anyone from using it. Do NOT encrypt any more data in this way, because it is NOT secure by today's standards. You should ONLY use decryption, for no other reasons than BACKWARD COMPATIBILITY, i.e. when you have no other choice. Want to encrypt? Use NaCl/libsodium if you possibly can.
Given the popularity of Python, at first I was disappointed that there was no complete answer to this question to be found. It took me a fair amount of reading different answers on this board, as well as other resources, to get it right. I thought I might share the result for future reference and perhaps review; I'm by no means a cryptography expert! However, the code below appears to work seamlessly:
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = ''
while len(d) < key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
Usage:
with open(in_filename, 'rb') as in_file, open(out_filename, 'wb') as out_file:
decrypt(in_file, out_file, password)
If you see a chance to improve on this or extend it to be more flexible (e.g. make it work without salt, or provide Python 3 compatibility), please feel free to do so.
Notice
This answer used to also concern encryption in Python using the same scheme. I have since removed that part to discourage anyone from using it. Do NOT encrypt any more data in this way, because it is NOT secure by today's standards. You should ONLY use decryption, for no other reasons than BACKWARD COMPATIBILITY, i.e. when you have no other choice. Want to encrypt? Use NaCl/libsodium if you possibly can.
I am re-posting your code with a couple of corrections (I didn't want to obscure your version). While your code works, it does not detect some errors around padding. In particular, if the decryption key provided is incorrect, your padding logic may do something odd. If you agree with my change, you may update your solution.
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = ''
while len(d) < key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
# This encryption mode is no longer secure by today's standards.
# See note in original question above.
def obsolete_encrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = Random.new().read(bs - len('Salted__'))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
out_file.write('Salted__' + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = bs - (len(chunk) % bs)
chunk += padding_length * chr(padding_length)
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
if padding_length < 1 or padding_length > bs:
raise ValueError("bad decrypt pad (%d)" % padding_length)
# all the pad-bytes must be the same
if chunk[-padding_length:] != (padding_length * chr(padding_length)):
# this is similar to the bad decrypt:evp_enc.c from openssl program
raise ValueError("bad decrypt")
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
The code below should be Python 3 compatible with the small changes documented in the code. Also wanted to use os.urandom instead of Crypto.Random. 'Salted__' is replaced with salt_header that can be tailored or left empty if needed.
from os import urandom
from hashlib import md5
from Crypto.Cipher import AES
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = b'' # changed '' to b''
while len(d) < key_length + iv_length:
# changed password to str.encode(password)
d_i = md5(d_i + str.encode(password) + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt(in_file, out_file, password, salt_header='', key_length=32):
# added salt_header=''
bs = AES.block_size
# replaced Crypt.Random with os.urandom
salt = urandom(bs - len(salt_header))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
# changed 'Salted__' to str.encode(salt_header)
out_file.write(str.encode(salt_header) + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = (bs - len(chunk) % bs) or bs
# changed right side to str.encode(...)
chunk += str.encode(
padding_length * chr(padding_length))
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, salt_header='', key_length=32):
# added salt_header=''
bs = AES.block_size
# changed 'Salted__' to salt_header
salt = in_file.read(bs)[len(salt_header):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(
in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = chunk[-1] # removed ord(...) as unnecessary
chunk = chunk[:-padding_length]
finished = True
out_file.write(bytes(x for x in chunk)) # changed chunk to bytes(...)
This answer is based on openssl v1.1.1, which supports a stronger key derivation process for AES encryption, than that of previous versions of openssl.
This answer is based on the following command:
echo -n 'Hello World!' | openssl aes-256-cbc -e -a -salt -pbkdf2 -iter 10000
This command encrypts the plaintext 'Hello World!' using aes-256-cbc. The key is derived using pbkdf2 from the password and a random salt, with 10,000 iterations of sha256 hashing. When prompted for the password, I entered the password, 'p4$$w0rd'. The ciphertext output produced by the command was:
U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=
The process for decrypting of the ciphertext above produced by openssl is as follows:
base64-decode the output from openssl, and utf-8 decode the
password, so that we have the underlying bytes for both of these.
The salt is bytes 8-15 of the base64-decoded openssl output.
Derive a 48-byte key using pbkdf2 given the password bytes and salt with
10,000 iterations of sha256 hashing.
The key is bytes 0-31 of the derived key, the iv is bytes 32-47 of the derived key.
The ciphertext is bytes 16 through the end of the base64-decoded openssl
output.
Decrypt the ciphertext using aes-256-cbc, given the key, iv, and
ciphertext.
Remove PKCS#7 padding from plaintext. The last byte of
plaintext indicates the number of padding bytes appended to the end
of the plaintext. This is the number of bytes to be removed.
Below is a python3 implementation of the above process:
import binascii
import base64
import hashlib
from Crypto.Cipher import AES #requires pycrypto
#inputs
openssloutputb64='U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE='
password='p4$$w0rd'
pbkdf2iterations=10000
#convert inputs to bytes
openssloutputbytes=base64.b64decode(openssloutputb64)
passwordbytes=password.encode('utf-8')
#salt is bytes 8 through 15 of openssloutputbytes
salt=openssloutputbytes[8:16]
#derive a 48-byte key using pbkdf2 given the password and salt with 10,000 iterations of sha256 hashing
derivedkey=hashlib.pbkdf2_hmac('sha256', passwordbytes, salt, pbkdf2iterations, 48)
#key is bytes 0-31 of derivedkey, iv is bytes 32-47 of derivedkey
key=derivedkey[0:32]
iv=derivedkey[32:48]
#ciphertext is bytes 16-end of openssloutputbytes
ciphertext=openssloutputbytes[16:]
#decrypt ciphertext using aes-cbc, given key, iv, and ciphertext
decryptor=AES.new(key, AES.MODE_CBC, iv)
plaintext=decryptor.decrypt(ciphertext)
#remove PKCS#7 padding.
#Last byte of plaintext indicates the number of padding bytes appended to end of plaintext. This is the number of bytes to be removed.
plaintext = plaintext[:-plaintext[-1]]
#output results
print('openssloutputb64:', openssloutputb64)
print('password:', password)
print('salt:', salt.hex())
print('key: ', key.hex())
print('iv: ', iv.hex())
print('ciphertext: ', ciphertext.hex())
print('plaintext: ', plaintext.decode('utf-8'))
As expected, the above python3 script produces the following:
openssloutputb64: U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=
password: p4$$w0rd
salt: ca7fc628e898c187
key: 444ab886d5721fc87e58f86f3e7734659007bea7fbe790541d9e73c481d9d983
iv: 7f4597a18096715d7f9830f0125be8fd
ciphertext: ea842d6862ac05ebefcf9b6cf4239711
plaintext: Hello World!
Note: An equivalent/compatible implementation in javascript (using the web crypto api) can be found at https://github.com/meixler/web-browser-based-file-encryption-decryption.
I know this is a bit late but here is a solution that I blogged in 2013 about how to use the python pycrypto package to encrypt/decrypt in an openssl compatible way. It has been tested on python2.7 and python3.x. The source code and a test script can be found here.
One of the key differences between this solution and the excellent solutions presented above is that it differentiates between pipe and file I/O which can cause problems in some applications.
The key functions from that blog are shown below.
# ================================================================
# get_key_and_iv
# ================================================================
def get_key_and_iv(password, salt, klen=32, ilen=16, msgdgst='md5'):
'''
Derive the key and the IV from the given password and salt.
This is a niftier implementation than my direct transliteration of
the C++ code although I modified to support different digests.
CITATION: http://stackoverflow.com/questions/13907841/implement-openssl-aes-encryption-in-python
#param password The password to use as the seed.
#param salt The salt.
#param klen The key length.
#param ilen The initialization vector length.
#param msgdgst The message digest algorithm to use.
'''
# equivalent to:
# from hashlib import <mdi> as mdf
# from hashlib import md5 as mdf
# from hashlib import sha512 as mdf
mdf = getattr(__import__('hashlib', fromlist=[msgdgst]), msgdgst)
password = password.encode('ascii', 'ignore') # convert to ASCII
try:
maxlen = klen + ilen
keyiv = mdf(password + salt).digest()
tmp = [keyiv]
while len(tmp) < maxlen:
tmp.append( mdf(tmp[-1] + password + salt).digest() )
keyiv += tmp[-1] # append the last byte
key = keyiv[:klen]
iv = keyiv[klen:klen+ilen]
return key, iv
except UnicodeDecodeError:
return None, None
# ================================================================
# encrypt
# ================================================================
def encrypt(password, plaintext, chunkit=True, msgdgst='md5'):
'''
Encrypt the plaintext using the password using an openssl
compatible encryption algorithm. It is the same as creating a file
with plaintext contents and running openssl like this:
$ cat plaintext
<plaintext>
$ openssl enc -e -aes-256-cbc -base64 -salt \\
-pass pass:<password> -n plaintext
#param password The password.
#param plaintext The plaintext to encrypt.
#param chunkit Flag that tells encrypt to split the ciphertext
into 64 character (MIME encoded) lines.
This does not affect the decrypt operation.
#param msgdgst The message digest algorithm.
'''
salt = os.urandom(8)
key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
if key is None:
return None
# PKCS#7 padding
padding_len = 16 - (len(plaintext) % 16)
if isinstance(plaintext, str):
padded_plaintext = plaintext + (chr(padding_len) * padding_len)
else: # assume bytes
padded_plaintext = plaintext + (bytearray([padding_len] * padding_len))
# Encrypt
cipher = AES.new(key, AES.MODE_CBC, iv)
ciphertext = cipher.encrypt(padded_plaintext)
# Make openssl compatible.
# I first discovered this when I wrote the C++ Cipher class.
# CITATION: http://projects.joelinoff.com/cipher-1.1/doxydocs/html/
openssl_ciphertext = b'Salted__' + salt + ciphertext
b64 = base64.b64encode(openssl_ciphertext)
if not chunkit:
return b64
LINELEN = 64
chunk = lambda s: b'\n'.join(s[i:min(i+LINELEN, len(s))]
for i in range(0, len(s), LINELEN))
return chunk(b64)
# ================================================================
# decrypt
# ================================================================
def decrypt(password, ciphertext, msgdgst='md5'):
'''
Decrypt the ciphertext using the password using an openssl
compatible decryption algorithm. It is the same as creating a file
with ciphertext contents and running openssl like this:
$ cat ciphertext
# ENCRYPTED
<ciphertext>
$ egrep -v '^#|^$' | \\
openssl enc -d -aes-256-cbc -base64 -salt -pass pass:<password> -in ciphertext
#param password The password.
#param ciphertext The ciphertext to decrypt.
#param msgdgst The message digest algorithm.
#returns the decrypted data.
'''
# unfilter -- ignore blank lines and comments
if isinstance(ciphertext, str):
filtered = ''
nl = '\n'
re1 = r'^\s*$'
re2 = r'^\s*#'
else:
filtered = b''
nl = b'\n'
re1 = b'^\\s*$'
re2 = b'^\\s*#'
for line in ciphertext.split(nl):
line = line.strip()
if re.search(re1,line) or re.search(re2, line):
continue
filtered += line + nl
# Base64 decode
raw = base64.b64decode(filtered)
assert(raw[:8] == b'Salted__' )
salt = raw[8:16] # get the salt
# Now create the key and iv.
key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
if key is None:
return None
# The original ciphertext
ciphertext = raw[16:]
# Decrypt
cipher = AES.new(key, AES.MODE_CBC, iv)
padded_plaintext = cipher.decrypt(ciphertext)
if isinstance(padded_plaintext, str):
padding_len = ord(padded_plaintext[-1])
else:
padding_len = padded_plaintext[-1]
plaintext = padded_plaintext[:-padding_len]
return plaintext
Tried everything above and some more from other threads,
this is what has worked for me, equivalent of this in openssl:
Not the best encrpython but those were requirements
Decryption: openssl enc -d -aes256 -md md5 -in {->path_in} -out {->path_out} -pass pass:{->pass}
Encryption: openssl enc -e -aes256 -md md5 -in {->path_in} -out {->path_out} -pass pass:{->pass}
Python:
from os import urandom
from hashlib import md5
from Crypto.Cipher import AES
import typer
def filecrypto(in_file, out_file, password, decrypt: bool = True):
salt_header = 'Salted__'
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = b'' # changed '' to b''
while len(d) < key_length + iv_length:
# changed password to str.encode(password)
d_i = md5(d_i + str.encode(password) + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt_f(in_file, out_file, password, salt_header=salt_header, key_length=32):
bs = AES.block_size
salt = urandom(bs - len(salt_header))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
with open(out_file, 'wb') as f_out:
# write the first line or the salted header
f_out.write(str.encode(salt_header) + salt)
with open(in_file, 'rb') as f_in:
f_out.write(cipher.encrypt(f_in.read()))
def decrypt_f(in_file, out_file, password, salt_header=salt_header, key_length=32):
bs = AES.block_size
with open(in_file, 'rb') as f_in:
# retrieve the salted header
salt = f_in.read(bs)[len(salt_header):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
with open(out_file, 'wb') as f_out:
f_out.write(cipher.decrypt(f_in.read()))
return decrypt_f(in_file, out_file, password) if decrypt else encrypt_f(in_file, out_file, password)
if __name__ == "__filecrypto__":
typer.run(filecrypto)
Note: this method is not OpenSSL compatible
But it is suitable if all you want to do is encrypt and decrypt files.
A self-answer I copied from here. I think this is, perhaps, a simpler and more secure option. Although I would be interested in some expert opinion on how secure it is.
I used Python 3.6 and SimpleCrypt to encrypt the file and then uploaded it.
I think this is the code I used to encrypt the file:
from simplecrypt import encrypt, decrypt
f = open('file.csv','r').read()
ciphertext = encrypt('USERPASSWORD',f.encode('utf8')) # I am not certain of whether I used the .encode('utf8')
e = open('file.enc','wb') # file.enc doesn't need to exist, python will create it
e.write(ciphertext)
e.close
This is the code I use to decrypt at runtime, I run getpass("password: ") as an argument so I don't have to store a password variable in memory
from simplecrypt import encrypt, decrypt
from getpass import getpass
# opens the file
f = open('file.enc','rb').read()
print('Please enter the password and press the enter key \n Decryption may take some time')
# Decrypts the data, requires a user-input password
plaintext = decrypt(getpass("password: "), f).decode('utf8')
print('Data have been Decrypted')
Note, the UTF-8 encoding behaviour is different in python 2.7 so the code will be slightly different.

How to decrypt OpenSSL AES-encrypted files in Python?

OpenSSL provides a popular (but insecure – see below!) command line interface for AES encryption:
openssl aes-256-cbc -salt -in filename -out filename.enc
Python has support for AES in the shape of the PyCrypto package, but it only provides the tools. How to use Python/PyCrypto to decrypt files that have been encrypted using OpenSSL?
Notice
This question used to also concern encryption in Python using the same scheme. I have since removed that part to discourage anyone from using it. Do NOT encrypt any more data in this way, because it is NOT secure by today's standards. You should ONLY use decryption, for no other reasons than BACKWARD COMPATIBILITY, i.e. when you have no other choice. Want to encrypt? Use NaCl/libsodium if you possibly can.
Given the popularity of Python, at first I was disappointed that there was no complete answer to this question to be found. It took me a fair amount of reading different answers on this board, as well as other resources, to get it right. I thought I might share the result for future reference and perhaps review; I'm by no means a cryptography expert! However, the code below appears to work seamlessly:
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = ''
while len(d) < key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
Usage:
with open(in_filename, 'rb') as in_file, open(out_filename, 'wb') as out_file:
decrypt(in_file, out_file, password)
If you see a chance to improve on this or extend it to be more flexible (e.g. make it work without salt, or provide Python 3 compatibility), please feel free to do so.
Notice
This answer used to also concern encryption in Python using the same scheme. I have since removed that part to discourage anyone from using it. Do NOT encrypt any more data in this way, because it is NOT secure by today's standards. You should ONLY use decryption, for no other reasons than BACKWARD COMPATIBILITY, i.e. when you have no other choice. Want to encrypt? Use NaCl/libsodium if you possibly can.
I am re-posting your code with a couple of corrections (I didn't want to obscure your version). While your code works, it does not detect some errors around padding. In particular, if the decryption key provided is incorrect, your padding logic may do something odd. If you agree with my change, you may update your solution.
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = ''
while len(d) < key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
# This encryption mode is no longer secure by today's standards.
# See note in original question above.
def obsolete_encrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = Random.new().read(bs - len('Salted__'))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
out_file.write('Salted__' + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = bs - (len(chunk) % bs)
chunk += padding_length * chr(padding_length)
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
if padding_length < 1 or padding_length > bs:
raise ValueError("bad decrypt pad (%d)" % padding_length)
# all the pad-bytes must be the same
if chunk[-padding_length:] != (padding_length * chr(padding_length)):
# this is similar to the bad decrypt:evp_enc.c from openssl program
raise ValueError("bad decrypt")
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
The code below should be Python 3 compatible with the small changes documented in the code. Also wanted to use os.urandom instead of Crypto.Random. 'Salted__' is replaced with salt_header that can be tailored or left empty if needed.
from os import urandom
from hashlib import md5
from Crypto.Cipher import AES
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = b'' # changed '' to b''
while len(d) < key_length + iv_length:
# changed password to str.encode(password)
d_i = md5(d_i + str.encode(password) + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt(in_file, out_file, password, salt_header='', key_length=32):
# added salt_header=''
bs = AES.block_size
# replaced Crypt.Random with os.urandom
salt = urandom(bs - len(salt_header))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
# changed 'Salted__' to str.encode(salt_header)
out_file.write(str.encode(salt_header) + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = (bs - len(chunk) % bs) or bs
# changed right side to str.encode(...)
chunk += str.encode(
padding_length * chr(padding_length))
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, salt_header='', key_length=32):
# added salt_header=''
bs = AES.block_size
# changed 'Salted__' to salt_header
salt = in_file.read(bs)[len(salt_header):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(
in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = chunk[-1] # removed ord(...) as unnecessary
chunk = chunk[:-padding_length]
finished = True
out_file.write(bytes(x for x in chunk)) # changed chunk to bytes(...)
This answer is based on openssl v1.1.1, which supports a stronger key derivation process for AES encryption, than that of previous versions of openssl.
This answer is based on the following command:
echo -n 'Hello World!' | openssl aes-256-cbc -e -a -salt -pbkdf2 -iter 10000
This command encrypts the plaintext 'Hello World!' using aes-256-cbc. The key is derived using pbkdf2 from the password and a random salt, with 10,000 iterations of sha256 hashing. When prompted for the password, I entered the password, 'p4$$w0rd'. The ciphertext output produced by the command was:
U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=
The process for decrypting of the ciphertext above produced by openssl is as follows:
base64-decode the output from openssl, and utf-8 decode the
password, so that we have the underlying bytes for both of these.
The salt is bytes 8-15 of the base64-decoded openssl output.
Derive a 48-byte key using pbkdf2 given the password bytes and salt with
10,000 iterations of sha256 hashing.
The key is bytes 0-31 of the derived key, the iv is bytes 32-47 of the derived key.
The ciphertext is bytes 16 through the end of the base64-decoded openssl
output.
Decrypt the ciphertext using aes-256-cbc, given the key, iv, and
ciphertext.
Remove PKCS#7 padding from plaintext. The last byte of
plaintext indicates the number of padding bytes appended to the end
of the plaintext. This is the number of bytes to be removed.
Below is a python3 implementation of the above process:
import binascii
import base64
import hashlib
from Crypto.Cipher import AES #requires pycrypto
#inputs
openssloutputb64='U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE='
password='p4$$w0rd'
pbkdf2iterations=10000
#convert inputs to bytes
openssloutputbytes=base64.b64decode(openssloutputb64)
passwordbytes=password.encode('utf-8')
#salt is bytes 8 through 15 of openssloutputbytes
salt=openssloutputbytes[8:16]
#derive a 48-byte key using pbkdf2 given the password and salt with 10,000 iterations of sha256 hashing
derivedkey=hashlib.pbkdf2_hmac('sha256', passwordbytes, salt, pbkdf2iterations, 48)
#key is bytes 0-31 of derivedkey, iv is bytes 32-47 of derivedkey
key=derivedkey[0:32]
iv=derivedkey[32:48]
#ciphertext is bytes 16-end of openssloutputbytes
ciphertext=openssloutputbytes[16:]
#decrypt ciphertext using aes-cbc, given key, iv, and ciphertext
decryptor=AES.new(key, AES.MODE_CBC, iv)
plaintext=decryptor.decrypt(ciphertext)
#remove PKCS#7 padding.
#Last byte of plaintext indicates the number of padding bytes appended to end of plaintext. This is the number of bytes to be removed.
plaintext = plaintext[:-plaintext[-1]]
#output results
print('openssloutputb64:', openssloutputb64)
print('password:', password)
print('salt:', salt.hex())
print('key: ', key.hex())
print('iv: ', iv.hex())
print('ciphertext: ', ciphertext.hex())
print('plaintext: ', plaintext.decode('utf-8'))
As expected, the above python3 script produces the following:
openssloutputb64: U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=
password: p4$$w0rd
salt: ca7fc628e898c187
key: 444ab886d5721fc87e58f86f3e7734659007bea7fbe790541d9e73c481d9d983
iv: 7f4597a18096715d7f9830f0125be8fd
ciphertext: ea842d6862ac05ebefcf9b6cf4239711
plaintext: Hello World!
Note: An equivalent/compatible implementation in javascript (using the web crypto api) can be found at https://github.com/meixler/web-browser-based-file-encryption-decryption.
I know this is a bit late but here is a solution that I blogged in 2013 about how to use the python pycrypto package to encrypt/decrypt in an openssl compatible way. It has been tested on python2.7 and python3.x. The source code and a test script can be found here.
One of the key differences between this solution and the excellent solutions presented above is that it differentiates between pipe and file I/O which can cause problems in some applications.
The key functions from that blog are shown below.
# ================================================================
# get_key_and_iv
# ================================================================
def get_key_and_iv(password, salt, klen=32, ilen=16, msgdgst='md5'):
'''
Derive the key and the IV from the given password and salt.
This is a niftier implementation than my direct transliteration of
the C++ code although I modified to support different digests.
CITATION: http://stackoverflow.com/questions/13907841/implement-openssl-aes-encryption-in-python
#param password The password to use as the seed.
#param salt The salt.
#param klen The key length.
#param ilen The initialization vector length.
#param msgdgst The message digest algorithm to use.
'''
# equivalent to:
# from hashlib import <mdi> as mdf
# from hashlib import md5 as mdf
# from hashlib import sha512 as mdf
mdf = getattr(__import__('hashlib', fromlist=[msgdgst]), msgdgst)
password = password.encode('ascii', 'ignore') # convert to ASCII
try:
maxlen = klen + ilen
keyiv = mdf(password + salt).digest()
tmp = [keyiv]
while len(tmp) < maxlen:
tmp.append( mdf(tmp[-1] + password + salt).digest() )
keyiv += tmp[-1] # append the last byte
key = keyiv[:klen]
iv = keyiv[klen:klen+ilen]
return key, iv
except UnicodeDecodeError:
return None, None
# ================================================================
# encrypt
# ================================================================
def encrypt(password, plaintext, chunkit=True, msgdgst='md5'):
'''
Encrypt the plaintext using the password using an openssl
compatible encryption algorithm. It is the same as creating a file
with plaintext contents and running openssl like this:
$ cat plaintext
<plaintext>
$ openssl enc -e -aes-256-cbc -base64 -salt \\
-pass pass:<password> -n plaintext
#param password The password.
#param plaintext The plaintext to encrypt.
#param chunkit Flag that tells encrypt to split the ciphertext
into 64 character (MIME encoded) lines.
This does not affect the decrypt operation.
#param msgdgst The message digest algorithm.
'''
salt = os.urandom(8)
key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
if key is None:
return None
# PKCS#7 padding
padding_len = 16 - (len(plaintext) % 16)
if isinstance(plaintext, str):
padded_plaintext = plaintext + (chr(padding_len) * padding_len)
else: # assume bytes
padded_plaintext = plaintext + (bytearray([padding_len] * padding_len))
# Encrypt
cipher = AES.new(key, AES.MODE_CBC, iv)
ciphertext = cipher.encrypt(padded_plaintext)
# Make openssl compatible.
# I first discovered this when I wrote the C++ Cipher class.
# CITATION: http://projects.joelinoff.com/cipher-1.1/doxydocs/html/
openssl_ciphertext = b'Salted__' + salt + ciphertext
b64 = base64.b64encode(openssl_ciphertext)
if not chunkit:
return b64
LINELEN = 64
chunk = lambda s: b'\n'.join(s[i:min(i+LINELEN, len(s))]
for i in range(0, len(s), LINELEN))
return chunk(b64)
# ================================================================
# decrypt
# ================================================================
def decrypt(password, ciphertext, msgdgst='md5'):
'''
Decrypt the ciphertext using the password using an openssl
compatible decryption algorithm. It is the same as creating a file
with ciphertext contents and running openssl like this:
$ cat ciphertext
# ENCRYPTED
<ciphertext>
$ egrep -v '^#|^$' | \\
openssl enc -d -aes-256-cbc -base64 -salt -pass pass:<password> -in ciphertext
#param password The password.
#param ciphertext The ciphertext to decrypt.
#param msgdgst The message digest algorithm.
#returns the decrypted data.
'''
# unfilter -- ignore blank lines and comments
if isinstance(ciphertext, str):
filtered = ''
nl = '\n'
re1 = r'^\s*$'
re2 = r'^\s*#'
else:
filtered = b''
nl = b'\n'
re1 = b'^\\s*$'
re2 = b'^\\s*#'
for line in ciphertext.split(nl):
line = line.strip()
if re.search(re1,line) or re.search(re2, line):
continue
filtered += line + nl
# Base64 decode
raw = base64.b64decode(filtered)
assert(raw[:8] == b'Salted__' )
salt = raw[8:16] # get the salt
# Now create the key and iv.
key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
if key is None:
return None
# The original ciphertext
ciphertext = raw[16:]
# Decrypt
cipher = AES.new(key, AES.MODE_CBC, iv)
padded_plaintext = cipher.decrypt(ciphertext)
if isinstance(padded_plaintext, str):
padding_len = ord(padded_plaintext[-1])
else:
padding_len = padded_plaintext[-1]
plaintext = padded_plaintext[:-padding_len]
return plaintext
Tried everything above and some more from other threads,
this is what has worked for me, equivalent of this in openssl:
Not the best encrpython but those were requirements
Decryption: openssl enc -d -aes256 -md md5 -in {->path_in} -out {->path_out} -pass pass:{->pass}
Encryption: openssl enc -e -aes256 -md md5 -in {->path_in} -out {->path_out} -pass pass:{->pass}
Python:
from os import urandom
from hashlib import md5
from Crypto.Cipher import AES
import typer
def filecrypto(in_file, out_file, password, decrypt: bool = True):
salt_header = 'Salted__'
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = b'' # changed '' to b''
while len(d) < key_length + iv_length:
# changed password to str.encode(password)
d_i = md5(d_i + str.encode(password) + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt_f(in_file, out_file, password, salt_header=salt_header, key_length=32):
bs = AES.block_size
salt = urandom(bs - len(salt_header))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
with open(out_file, 'wb') as f_out:
# write the first line or the salted header
f_out.write(str.encode(salt_header) + salt)
with open(in_file, 'rb') as f_in:
f_out.write(cipher.encrypt(f_in.read()))
def decrypt_f(in_file, out_file, password, salt_header=salt_header, key_length=32):
bs = AES.block_size
with open(in_file, 'rb') as f_in:
# retrieve the salted header
salt = f_in.read(bs)[len(salt_header):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
with open(out_file, 'wb') as f_out:
f_out.write(cipher.decrypt(f_in.read()))
return decrypt_f(in_file, out_file, password) if decrypt else encrypt_f(in_file, out_file, password)
if __name__ == "__filecrypto__":
typer.run(filecrypto)
Note: this method is not OpenSSL compatible
But it is suitable if all you want to do is encrypt and decrypt files.
A self-answer I copied from here. I think this is, perhaps, a simpler and more secure option. Although I would be interested in some expert opinion on how secure it is.
I used Python 3.6 and SimpleCrypt to encrypt the file and then uploaded it.
I think this is the code I used to encrypt the file:
from simplecrypt import encrypt, decrypt
f = open('file.csv','r').read()
ciphertext = encrypt('USERPASSWORD',f.encode('utf8')) # I am not certain of whether I used the .encode('utf8')
e = open('file.enc','wb') # file.enc doesn't need to exist, python will create it
e.write(ciphertext)
e.close
This is the code I use to decrypt at runtime, I run getpass("password: ") as an argument so I don't have to store a password variable in memory
from simplecrypt import encrypt, decrypt
from getpass import getpass
# opens the file
f = open('file.enc','rb').read()
print('Please enter the password and press the enter key \n Decryption may take some time')
# Decrypts the data, requires a user-input password
plaintext = decrypt(getpass("password: "), f).decode('utf8')
print('Data have been Decrypted')
Note, the UTF-8 encoding behaviour is different in python 2.7 so the code will be slightly different.

Categories

Resources