pycrypto encrypt/decrypt, losing part of encrypted string when decrypting - python

I am trying to encrypt/decrypt with pycrypto in python. for the most part things have worked smooth but I am getting an odd problem when decrypting data.I have tried to encrypt/decrypt some jpgs for testing and although they encrypt/decrypt without issue, the decrypted files cannot be opened/are corrupted. To try to find the problem I saved a textfile with a random sentence similar to "test this file for integrity blah blah blah" and it decrypts correctly only after ".... integrity blah blah blah", everything before integrity is still in garbled characters. I'm not that knowledgable on AES, but im assuming that this is an encoding/decoding or padding error.
Here is my code:
#encryption
iv = Random.new().read( AES.block_size)
filePath = input("Path to file for encryption: ")
selFile = open(filePath, 'rb')
getBytes = bytes(selFile.read())
encPW = input("Enter password: ")
hashpw = hashlib.sha256(encPW.encode('UTF-8').digest())
destination = input("Destination path for encrypted file: ")
aes = AES.new(hashpw, AES.Mode_CFB, iv)
encFile = base65.b64encode(aes.encrypt(getBytes))
writetofile = open(destination, 'wb')
writetofile.write(encFile)
writetofile.close()
print("Encryption successful")
#Decryption
iv = Random.new().read( AES.block_size)
filePath = input("Path to file for decryption: ")
selFile = open(filePath, 'rb')
getBytes = bytes(selFile.read())
decPW = input("Enter password: ")
hashdecpw = hashlib.sha256(encPW.encode('UTF-8').digest())
destination = input("Destination path for decrypted file: ")
aes = AES.new(hashdecpw, AES.Mode_CFB, iv)
decFile = aes.decrypt(getBytes)
writetofile = open(destination, 'wb')
writetofile.write(decFile)
writetofile.close()
print("Decryption successful")
Any ideas on what could be causing the loss of the first characters, and preventing me from encrypting/decrypting files correctly?

You have at least three issues:
You probably mean hashlib.sha256(encPW.encode('UTF-8')).digest() instead of hashlib.sha256(encPW.encode('UTF-8').digest()) (the closing brace is at the wrong position)
You're encoding the ciphertext with Base64 before writing it to a file. You've forgot to decode it after reading it back from the file before decrypting it. For example:
getBytes = base64.b64decode(bytes(selFile.read()))
This is the big one: You need the exact same IV during the decryption that you've used for encryption. The IV is not secret, but it needs to be unique for every encryption that you've done with the same key. Commonly the IV is written in front of the ciphertext and read back for decryption.
#encryption
encFile = base64.b64encode(iv + aes.encrypt(getBytes))
#decryption
getBytes = base64.b64decode(bytes(selFile.read()))
iv = getBytes[:16]
aes = AES.new(hashdecpw, AES.Mode_CFB, iv)
decFile = aes.decrypt(getBytes[16:])

You're generating a new IV for encryption and decryption seperately, which comes to yield such problems. Here's what I recommend doing:
def encrypt(inpath, outpath, password):
iv = Random.new().read(AES.block_size)
with open(inpath, "rb") as f:
contents = f.read()
# A context manager automatically calls f.close()
key = pbkdf2.crypt(password, "")
# See notes
aes = AES.new(key, AES.Mode_CFB, iv)
encrypted = aes.encrypt(contents)
with open(outpath, "wb") as f:
f.write(iv + b":")
f.write(encrypted)
print("Encryption successful")
def decrypt(inpath, outpath, password):
with open(inpath, "rb") as f:
contents = f.read()
iv, encrypted = contents.split(b":")
key = pbkdf2.crypt(password, "")
aes = AES.new(key, AES.Mode_CFB, iv)
decrypted = aes.decrypt(contents)
with open(outpath, "wb") as f:
f.write(decrypted)
print("Decryption successful")
Some notes:
An IV is not meant to be secret, so it can be randomly generated once and then written to a file to be used later for decryption (as shown in this example)
A hashing algorithm is not strong enough for deriving keys, which is why there are special tools called key derivation algorithms (like PBKDF2 in python). Use those instead!
I have not tested this code myself, so it may not work properly.

Related

How do i encrypt and decrypt files with PyCryptodome? (RSA)

I'm trying to encrypt and decrypt a file with PyCryptodome but without success. I can encrypt strings and data just fine but when trying to encrypt files it fails. I have 2 problems, first is that I can't encrypt larger strings. Witch i tried to solve by reading the file with a buffer. Second is that when I try to encrypt it as smaller buffers it just gives me an error "raise ValueError("Ciphertext with incorrect length.")"
My code looks like this:
from Crypto.Cipher import PKCS1_OAEP
import binascii
import ast
file_to_encrypt = "file_example_MP3_700KB.mp3"
buffer_size = 65536 # 64kb
input_file = open(file_to_encrypt, "rb")
output_file = open(file_to_encrypt + ".encrypted", "wb")
# Import keys
pub = open("publickey.txt", "rb")
pubKey = RSA.importKey(pub.read())
pub.close()
priv = open("privatekey.txt", "rb")
keyPair = RSA.importKey(priv.read())
priv.close()
# --------------------------------------------------------------
# Encrypt
encryptor = PKCS1_OAEP.new(pubKey)
buffer = input_file.read(buffer_size)
while len(buffer) > 0:
encrypted = encryptor.encrypt(buffer)
output_file.write(encrypted)
buffer = input_file.read(buffer_size)
input_file.close()
output_file.close()
# --------------------------------------------------------------
input_file = open(file_to_encrypt + ".encrypted", "rb")
output_file = open(file_to_encrypt + ".decrypted", "wb")
# Decrypt
decryptor = PKCS1_OAEP.new(keyPair)
buffer = input_file.read(buffer_size)
while len(buffer) > 0:
decrypted = decryptor.decrypt(ast.literal_eval(str(buffer)))
output_file.write(decrypted)
buffer = input_file.read(buffer_size)
input_file.close()
output_file.close()
# --------------------------------------------------------------
And generating the keys looks like this:
from Crypto.Cipher import PKCS1_OAEP
import binascii
import ast
# key generation
keyPair = RSA.generate(3072*2)
pubKey = keyPair.publickey()
# --------------------------------------------------------------
# Export keys
pub = open("publickey.txt", "wb")
pub.write(pubKey.exportKey('PEM'))
pub.close()
priv = open("privatekey.txt", "wb")
priv.write(keyPair.exportKey('PEM'))
priv.close()
# --------------------------------------------------------------
# Import keys
pub = open("publickey.txt", "rb")
pubKey = RSA.importKey(pub.read())
pub.close()
priv = open("privatekey.txt", "rb")
keyPair = RSA.importKey(priv.read())
priv.close()
# --------------------------------------------------------------
# encryption
msg = '550011'
encryptor = PKCS1_OAEP.new(pubKey)
encrypted = encryptor.encrypt(msg.encode())
# --------------------------------------------------------------
# decryption
decryptor = PKCS1_OAEP.new(keyPair)
decrypted = str(decryptor.decrypt(ast.literal_eval(str(encrypted))))[2:-1]
# --------------------------------------------------------------
print("Encrypted:", binascii.hexlify(encrypted))
print("Decrypted:", decrypted)
if msg == decrypted:
print("PASSED!")
else:
print("FAILED!")
Changing buffer_size fixes the first problem (that the data I'm trying to encrypt is too large.)
But I still can't decrypt my file after encrypting it.
Generating and importing keys works just fine. And encrypting and decrypting with them works just fine as well. As long as I'm only encrypting small strings and not files.

TypeError: can't concat str to bytes - Python + pycryptodome

I'm receiving a TypeError when trying to encrypt + decrypt a file using pycryptodome. I've looked around but can't find much relevant to pycryptodome regarding this. The error is:
Error Image
The code I'm using is:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import binascii
print("Now executing RSA")
# generate the RSA keys and print them on the console (as hex numbers and in the PKCS#8 PEM ASN.1 format)
keyPair = RSA.generate(3072)
pubKey = keyPair.publickey()
print(f"Public key: (n={hex(pubKey.n)}, e={hex(pubKey.e)})")
pubKeyPEM = pubKey.exportKey()
print(pubKeyPEM.decode('ascii'))
print(f"Private key: (n={hex(pubKey.n)}, d={hex(keyPair.d)})")
privKeyPEM = keyPair.exportKey()
print(privKeyPEM.decode('ascii'))
# encrypt the message using RSA-OAEP encryption scheme (RSA with PKCS#1 OAEP padding) with the RSA public key
# msg = b'A message for encryption'
f = open("plaintext.txt", "r")
f = f.read()
encryptor = PKCS1_OAEP.new(pubKey)
encrypted = encryptor.encrypt(f)
print("Encrypted:", binascii.hexlify(encrypted))
# decrypt the message using RSA-OAEP with the RSA Private key
decryptor = PKCS1_OAEP.new(keyPair)
decrypted = decryptor.decrypt(encrypted)
print('Decrypted:', decrypted)
You need to convert plaintext into bytes or read file as binary data. add b to read file as binary data.
f = open("plaintext.txt", "rb")
A side note You should consider closing the file after finishing using it
file = open("plaintext.txt", "rb")
f = file.read()
encryptor = PKCS1_OAEP.new(pubKey)
encrypted = encryptor.encrypt(f)
print("Encrypted:", binascii.hexlify(encrypted))
file.close()
Make sure both data types are equal. You can try converting the text file to bytes so the data types are equal, and hence can be concatenated.

Convert hex() to pycryptodome ciphertext format

from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
new_file = open("md5_rehashed.txt", "w")
file_with_hashes = open('md5_hashed.txt', 'r')
key = RSA.importKey(open(received_message.decode('utf-8')).read())
cipher = PKCS1_OAEP.new(key)
for hash in file_with_hashes:
message = bytes(hash, 'utf-8')
ciphertext = cipher.encrypt(message)
new_file.write(ciphertext.hex().upper()+'\n')
file_with_hashes.close()
new_file.close()
Im using PKCS1_OAEP to encrypt strings stored in a local file,
next i convert the ciphertext to hex format and writing this str in a new .txt file.
In another file.py i need to read the new .txt file and decrypt all hashes of file, the problem is i can't find the form to comeback of ciphertext.hex() format to ciphertext format.
any suggestions?
here is my code of the second file.py
hashed_file = open(hashed_file_route, 'r')
unhashed_file = open('md5_unhashed', 'w')
key = RSA.importKey(open('private.pem').read())
cipher = PKCS1_OAEP.new(key)
for hash_line in hashed_file:
print(hash_line)
print(codecs.encode(bytes(hash_line,'utf-8'),'hex_codec'))
message = cipher.decrypt(hash_line.encode('utf-8'))
unhashed_file.write(message+ '\n')
unhashed_file.close()
hashed_file.close()
PS: sorry for my english
You should use bytes.fromhex(some_hex_string) to recover bytes from hexadecimal strings, as follows:
message = cipher.decrypt(bytes.fromhex(hash_line))

How to decrypt a RC2 ciphertext?

Python 3.5, pycrypto 2.7a1, Windows, RC2 ciphering
Example:
print('Введите текс, который хотите зашифровать:')
text = input()
with open('plaintext.txt', 'w') as f:
f.write(text)
key = os.urandom(32)
with open('rc2key.bin', 'wb') as keyfile:
keyfile.write(key)
iv = Random.new().read(ARC2.block_size)
cipher = ARC2.new(key, ARC2.MODE_CFB, iv)
ciphertext = iv + cipher.encrypt(bytes(text, "utf-8"))
with open('iv.bin', 'wb') as f:
f.write(iv)
with open('ciphertext.bin', 'wb') as f:
f.write(ciphertext)
print(ciphertext.decode("cp1251"))
And I'd like to know how can I decrypt this text, I tried, but couldn't do it.
My try to decrypt:
os.system('cls')
print('Дешифруем значит')
with open('ciphertext.bin', 'rb') as f:
ciphertext = f.read()
with open('rc2key.bin', 'rb') as f:
key = f.read()
with open('iv.bin', 'rb') as f:
iv = f.read()
ciphertext = ciphertext.decode('cp1251')
iv = iv.decode('cp1251')
text = ciphertext.replace(iv, '')
text = cipher.decrypt(text)
with open('plaintext.txt', 'w') as f:
f.write(text)
print(text.decode("ascii"))
But I understood that I need cipher variable, and I can't save it to .txt or .bin file, so that why I'm asking for help.
The IV is a non-secret value and is commonly written in front of the ciphertext. Since, you've done that already, you don't need to write an additional IV file. RC2 has a block size of 64 bit, so the IV will always be 8 byte long.
with open('ciphertext.bin', 'rb') as f:
ciphertext = f.read()
with open('rc2key.bin', 'rb') as f:
key = f.read()
iv = ciphertext[:ARC2.block_size]
ciphertext = ciphertext[ARC2.block_size:]
cipher = ARC2.new(key, ARC2.MODE_CFB, iv)
text = cipher.decrypt(ciphertext).decode("utf-8")
with open('plaintext.txt', 'w') as f:
f.write(text)
print(text)
Other problems:
Don't simply decode binary data such as ciphertexts, keys or IV, because those are most likely not printable.
Don't re-use the same cipher object if you're doing something different. The decryption needs a freshly initialized ARC2 object.

Decrypting a file to a stream and reading the stream into pandas (hdf or stata)

Overview of what I'm trying to do. I have encrypted versions of files that I need to read into pandas. For a couple of reasons it is much better to decrypt into a stream rather than a file, so that's my interest below although I also attempt to decrypt to a file just as an intermediate step (but this also isn't working).
I'm able to get this working for a csv, but not for either hdf or stata (I'd accept an answer that works for either hdf or stata, though the answer might be the same for both, which is why I'm combining in one question).
The code for encrypting/decrypting files is taken from another stackoverflow question (which I can't find at the moment).
import pandas as pd
import io
from Crypto import Random
from Crypto.Cipher import AES
def pad(s):
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def encrypt(message, key, key_size=256):
message = pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(message)
def decrypt(ciphertext, key):
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
return plaintext.rstrip(b"\0")
def encrypt_file(file_name, key):
with open(file_name, 'rb') as fo:
plaintext = fo.read()
enc = encrypt(plaintext, key)
with open(file_name + ".enc", 'wb') as fo:
fo.write(enc)
def decrypt_file(file_name, key):
with open(file_name, 'rb') as fo:
ciphertext = fo.read()
dec = decrypt(ciphertext, key)
with open(file_name[:-4], 'wb') as fo:
fo.write(dec)
And here's my attempt to extend the code to decrypt to a stream rather than a file.
def decrypt_stream(file_name, key):
with open(file_name, 'rb') as fo:
ciphertext = fo.read()
dec = decrypt(ciphertext, key)
cipherbyte = io.BytesIO()
cipherbyte.write(dec)
cipherbyte.seek(0)
return cipherbyte
Finally, here's the sample program with sample data attempting to make this work:
key = 'this is an example key'[:16]
df = pd.DataFrame({ 'x':[1,2], 'y':[3,4] })
df.to_csv('test.csv',index=False)
df.to_hdf('test.h5','test',mode='w')
df.to_stata('test.dta')
encrypt_file('test.csv',key)
encrypt_file('test.h5',key)
encrypt_file('test.dta',key)
decrypt_file('test.csv.enc',key)
decrypt_file('test.h5.enc',key)
decrypt_file('test.dta.enc',key)
# csv works here but hdf and stata don't
# I'm less interested in this part but include it for completeness
df_from_file = pd.read_csv('test.csv')
df_from_file = pd.read_hdf('test.h5','test')
df_from_file = pd.read_stata('test.dta')
# csv works here but hdf and stata don't
# the hdf and stata lines below are what I really need to get working
df_from_stream = pd.read_csv( decrypt_stream('test.csv.enc',key) )
df_from_stream = pd.read_hdf( decrypt_stream('test.h5.enc',key), 'test' )
df_from_stream = pd.read_stata( decrypt_stream('test.dta.enc',key) )
Unfortunately I don't think I can shrink this code anymore and still have a complete example.
Again, my hope would be to have all 4 non-working lines above working (file and stream for hdf and stata) but I'm happy to accept an answer that works for either the hdf stream alone or the stata stream alone.
Also, I'm open to other encryption alternatives, I just used some existing pycrypto-based code that I found here on SO. My work explicitly requires 256-bit AES but beyond that I'm open so this solution needn't be based specifically on the pycrypto library or the specific code example above.
Info on my setup:
python: 3.4.3
pandas: 0.17.0 (anaconda 2.3.0 distribution)
mac os: 10.11.3
The biggest issue is the padding/unpadding method. It assumes that the null character can't be part of the actual content. Since stata/hdf files are binary, it's safer to pad using the number of extra bytes we use, encoded as a character. This number will be used during unpadding.
Also for this time being, read_hdf doesn't support reading from a file like object, even if the API documentation claims so. If we restrict ourselves to the stata format, the following code will perform what you need:
import pandas as pd
import io
from Crypto import Random
from Crypto.Cipher import AES
def pad(s):
n = AES.block_size - len(s) % AES.block_size
return s + n * chr(n)
def unpad(s):
return s[:-ord(s[-1])]
def encrypt(message, key, key_size=256):
message = pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(message)
def decrypt(ciphertext, key):
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
return unpad(plaintext)
def encrypt_file(file_name, key):
with open(file_name, 'rb') as fo:
plaintext = fo.read()
enc = encrypt(plaintext, key)
with open(file_name + ".enc", 'wb') as fo:
fo.write(enc)
def decrypt_stream(file_name, key):
with open(file_name, 'rb') as fo:
ciphertext = fo.read()
dec = decrypt(ciphertext, key)
cipherbyte = io.BytesIO()
cipherbyte.write(dec)
cipherbyte.seek(0)
return cipherbyte
key = 'this is an example key'[:16]
df = pd.DataFrame({
'x': [1,2],
'y': [3,4]
})
df.to_stata('test.dta')
encrypt_file('test.dta', key)
print pd.read_stata(decrypt_stream('test.dta.enc', key))
Output:
index x y
0 0 1 3
1 1 2 4
In python 3 you can use the following pad, unpad versions:
def pad(s):
n = AES.block_size - len(s) % AES.block_size
return s + bytearray([n] * n)
def unpad(s):
return s[:-s[-1]]
What worked for me in the case of .h5 format and the cryptography library was:
from cryptography.fernet import Fernet
def read_h5_file(new_file:str, decrypted: bytes, verbose=False):
with open(new_file, 'wb') as f:
f.write(decrypted)
print(f'Created {new_file}') if verbose else ''
df = pd.read_hdf(new_file)
os.remove(new_file)
print(f'Deleted {new_file}') if verbose else ''
return df
with open(path_to_file, 'rb') as f:
data = f.read()
fernet = Fernet(key)
decrypted = fernet.decrypt(data)
new_file = './example_path/example.h5'
df = read_h5_file(new_file, decrypted, verbose=verbose)
So I created a .h5 file. Read its content. Return it with the function. Delete the decrypted file again.
Maybe this approach helps, as I didn't find any other or similar solution on this online.

Categories

Resources