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.
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.
I have a encrypted message in a file, encrypted by the following code.
I wrote a function to decrypt this message. I know the password used to encrypt it.
But I got the following error:
python3 decrypt.py enim_msg.txt
Traceback (most recent call last):
File "decrypt.py", line 45, in <module>
print(":: Decrypted: \n" + bytes.decode(decrypted))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x88 in position 2: invalid start byte
How can I fix this problem pls ?
Is My decrypt function wrong ?
My code :
Encrypt function
import os
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
def encrypt(key, filename):
chunksize = 64*1024
outputFile = "en" + filename
filesize = str(os.path.getsize(filename)).zfill(16)
IV = Random.new().read(16)
encryptor = AES.new(key, AES.MODE_CBC, IV)
with open(filename, 'rb') as infile:
with open(outputFile, 'wb') as outfile:
outfile.write(filesize.encode('utf-8'))
outfile.write(IV)
while True:
chunk = infile.read(chunksize)
if len(chunk) == 0:
break
elif len(chunk) % 16 != 0:
chunk += b' ' * (16 - (len(chunk) % 16))
outfile.write(encryptor.encrypt(chunk))
def getKey(password):
hasher = SHA256.new(password.encode('utf-8'))
return hasher.digest()
Decrypt function I wrote
def decrypt(enc, password):
#print(":: enc => " + enc)
private_key = hashlib.sha256(password.encode("utf-8")).digest()
iv = enc[:16]
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return cipher.decrypt(enc[16:])
How I call this function
password = "azerty123"
secret_file_path = sys.argv[1]
the_file = open(secret_file_path, "rb")
encrypted = the_file.read()
decrypted = decrypt(encrypted, password)
the_file.close()
print(":: Decrypted: \n" + bytes.decode(decrypted))
The bytes.decrypt() function by default expects an UTF-8 encoded string. But not every sequence of bytes is a valid UTF-8 sequence. In your case cipher.decrypt() (which may return any sequence of bytes) returned a byte-sequence, which is not a valid UTF-8 sequence. Thus the bytes.decode() function raised an error.
The actual reason why cipher.decrypt() returned a non-UTF-8 string is a bug in your code:
Your encrypted file format contains non-utf-8 data. Its format is like:
16 bytes len info (unencrypted, UTF-8 encoded)
16 bytes IV (unencrypted, binary i.e. non-UTF-8 encoded)
n bytes payload (encrypted, UTF-8 encoded)
You have to ensure that on decryption you only decode parts of your file, that are UTF-8 encoded. Furthermore you have to ensure that you decrypt only encrypted parts of your file (as mentioned in your comments)
I'm trying to encrypt the data in a file using AES encryption and then encrypt the AES key using RSA. But when i try to read the keys from the file it crops up with the error "RSA binascii.Error: Incorrect padding".
Traceback (most recent call last):
File "C:/Users/dbane_000/PycharmProjects/RSE/RSA.py", line 33, in <module>
key=RSA.importKey(f.read())
File "C:\Python27\lib\site-packages\Crypto\PublicKey\RSA.py", line 660, in importKey
der = binascii.a2b_base64(b('').join(lines[1:-1]))
binascii.Error: Incorrect padding
The error doesn't come always but maybe once for every five times that I run this code. What could be the reason?
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto import Random
import rsa
import base64
import os
BLOCK_SIZE = 32
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
random_generator = Random.new().read
rsakey = RSA.generate(1024, random_generator)
f=open('key.pem','w')
cipher = PKCS1_OAEP.new(rsakey.publickey())
f.write(rsakey.exportKey("PEM"))
f.write(rsakey.publickey().exportKey("PEM"))
f.close()
f=open('key.pem','r')
key=RSA.importKey(f.read())
pubkey=key.publickey()
f.close()
secret = os.urandom(BLOCK_SIZE)
print secret
crypto =pubkey.encrypt(secret, 32)
secret =key.decrypt(crypto)
print crypto
print secret
cipher = AES.new(secret)
# encode a string
f=open('plaintext.txt','r')
plaintext=f.read()
f.close()
encoded = EncodeAES(cipher, plaintext)
print 'Encrypted string:', encoded
f=open('cipher_data.txt','w')
f.write(encoded)
f.close()
# decode the encoded string
decoded = DecodeAES(cipher, encoded)
print 'Decrypted string:', decoded
f=open('plaintext.txt','r')
plaintext=f.read()
f.close()
f=open('decrypted.txt','w')
f.write(decoded)
f.close()
Please try to check the contents of the input files from where you are taking key or data to encrypt or decrypt. This type of error would may occur,if the data from f.read() is in a desired format to perform decrypt.. And please try to write those keys or data at begining or desired index and fetch from that index..
# please check at this statements
f=open('key.pem','w')
cipher = PKCS1_OAEP.new(rsakey.publickey())
f.write(rsakey.exportKey("PEM"))
f.write(rsakey.publickey().exportKey("PEM"))
f.close()
f=open('key.pem','r')
key=RSA.importKey(f.read())
pubkey=key.publickey()
f.close()
I hope this may help you..
I just found pycrypto today, and I've been working on my AES encryption class. Unfortunately it only half-works. self.h.md5 outputs md5 hash in hex format, and is 32byte.
This is the output. It seems to decrypt the message, but it puts random characters after decryption, in this case \n\n\n... I think I have a problem with block size of self.data, anyone know how to fix this?
Jans-MacBook-Pro:test2 jan$ ../../bin/python3 data.py
b'RLfGmn5jf5WTJphnmW0hXG7IaIYcCRpjaTTqwXR6yiJCUytnDib+GQYlFORm+jIctest
1 2 3 4 5 endtest\n\n\n\n\n\n\n\n\n\n'
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from os import urandom
class Encryption():
def __init__(self):
self.h = Hash()
def values(self, data, key):
self.data = data
self.key = key
self.mode = AES.MODE_CBC
self.iv = urandom(16)
if not self.key:
self.key = Cfg_Encrypt_Key
self.key = self.h.md5(self.key, True)
def encrypt(self, data, key):
self.values(data, key)
return b64encode(self.iv + AES.new(self.key, self.mode, self.iv).encrypt(self.data))
def decrypt(self, data, key):
self.values(data, key)
self.iv = b64decode(self.data)[:16]
return AES.new(self.key, self.mode, self.iv).decrypt(b64decode(self.data)[16:])
To be honest, the characters "\n\n\n\n\n\n\n\n\n\n" don't look that random to me. ;-)
You are using AES in CBC mode. That requires length of plaintext and ciphertext to be always a multiple of 16 bytes. With the code you show, you should actually see an exception being raised when data passed to encrypt() does not fulfill such condition. It looks like you added enough new line characters ('\n' to whatever the input is until the plaintext happened to be aligned.
Apart from that, there are two common ways to solve the alignment issue:
Switch from CBC (AES.MODE_CBC) to CFB (AES.MODE_CFB). With the default segment_size used by PyCrypto, you will not have any restriction on plaintext and ciphertext lengths.
Keep CBC and use a padding scheme like PKCS#7, that is:
before encrypting a plaintext of X bytes, append to the back as many bytes you need to to reach the next 16 byte boundary. All padding bytes have the same value: the number of bytes that you are adding:
length = 16 - (len(data) % 16)
data += bytes([length])*length
That's Python 3 style. In Python 2, you would have:
length = 16 - (len(data) % 16)
data += chr(length)*length
after decrypting, remove from the back of the plaintext as many bytes as indicated by padding:
data = data[:-data[-1]]
Even though I understand in your case it is just a class exercise, I would like to point out that it is insecure to send data without any form of authentication (e.g. a MAC).
You can use a fix character as long as you remember the length of your initial payload, so you don't "throw" useful end bytes away.
Try this:
import base64
from Crypto.Cipher import AES
def encrypt(payload, salt, key):
return AES.new(key, AES.MODE_CBC, salt).encrypt(r_pad(payload))
def decrypt(payload, salt, key, length):
return AES.new(key, AES.MODE_CBC, salt).decrypt(payload)[:length]
def r_pad(payload, block_size=16):
length = block_size - (len(payload) % block_size)
return payload + chr(length) * length
print(decrypt(encrypt("some cyphertext", "b" * 16, "b" * 16), "b" * 16, "b" * 16, len("some cyphertext")))
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
import base64
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 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)
#print in_file
in_file = file(in_file, 'rb')
out_file = file(out_file, 'wb')
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))
in_file.close()
out_file.close()
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
in_file = file(in_file, 'rb')
out_file = file(out_file, 'wb')
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)
in_file.close()
out_file.close()
def encode(in_file, out_file):
in_file = file(in_file, 'rb')
out_file = file(out_file, 'wb')
data = in_file.read()
out_file.write(base64.b64encode(data))
in_file.close()
out_file.close()
def decode(in_file, out_file):
in_file = file(in_file, 'rb')
out_file = file(out_file, 'wb')
data = in_file.read()
out_file.write(base64.b64decode(data))
in_file.close()
out_file.close()
AES.new().encrypt() and .decrypt() take as both input and output strings whose length is a multiple of 16. You have to fix it in one way or another. For example you can store the real length at the start, and use that to truncate the decrypted string.
Note also that while it's the only restriction for AES, other modules (notably in Crypto.PublicKey) have additional restrictions that comes from their mathematical implementation and that shouldn't (in my opinion) be visible to the end user, but are. For example Crypto.PublicKey.ElGamal will encrypt any short string, but if it starts with null characters, they are lost upon decryption.