Tom Detranchant
Tom Detranchant

Reputation: 71

PyCrypto Cyphertext incorrect length even with a one char data

I want to encrypt/decrypt a set of data which is contained in a .csv file. I generate my RSA public/private keys with this code :

import Crypto
from Crypto.PublicKey import RSA

key = RSA.generate(2048)

k = key.exportKey('PEM')
p = key.publickey().exportKey('PEM')

with open('private.pem', 'w') as kf:
    kf.write(k.decode())
    kf.close()

with open('public.pem', 'w') as pf:
    pf.write(p.decode())
    pf.close()

with open('private.pem','r') as fk:
    priv = fk.read()
    fk.close()

with open('public.pem','r') as fp:
    pub = fp.read()
    fp.close()

privat = RSA.importKey(priv)
public = RSA.importKey(pub)

if key == privat:
    print('Private key has been successfuly write')
if key.publickey() == public:
    print('Public key has been successfuly write')

Then I encrypt with this code without any problem:

import Crypto
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

with open('public.pem','r') as fp:
    pub = fp.read()
    fp.close()

public = RSA.importKey(pub)

#stockage du fichier dans une variable rep
fichier = open('test.csv', 'r')
rep = fichier.read()
fichier.close()

#eliminations des spaces
rep = rep.replace(' ', '')

#encodage pour type bytes
rep = rep.encode()

#decoupage en mot de 10 chars
rep = [rep[i:i+10] for i in range(0, len(rep), 10)]

cipher = PKCS1_OAEP.new(public)

fichier2 = open('encrypted.csv', 'a')
for i in rep:
    encrypted_line = cipher.encrypt(i)
    fichier2.write(str(encrypted_line))
    fichier2.write('\n')

fichier2.close()

I can modify how my data are separate by modifying this line :

rep = [rep[i:i+n] for i in range(0, len(rep), n)]

This line separate my data by groups of n chars

Here my code to decrypt the data, it raises:

ValueError: Ciphertext with incorrect length.

import Crypto
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

with open('private.pem','r') as fk:
    priv = fk.read()
    fk.close()

private = RSA.importKey(priv)

fichier = open('encrypted.csv', 'r')
rep = fichier.read().splitlines()
fichier.close()

cipher = PKCS1_OAEP.new(private)

fichier2 = open('decrypted.csv', 'a')
for i in rep:
    decrypted_line = cipher.decrypt(i)
    decrypted_line = decrypted_line.decode('utf-8')
    fichier2.write(str(encrypted_line))

fichier2.close()

I tried to encode a sample file and it raised this ValueError . Then I try to work with a file which contain only one char directly on the Python interpreter. Encryption worked well but decryption broke with the same error as above.

Upvotes: 3

Views: 1858

Answers (1)

snakecharmerb
snakecharmerb

Reputation: 55589

The main problem with the code is the use of newline characters to separate the chunks of encrypted data. The encrypted data may already contain newline characters, so attempting to split the encrypted data into lines may produce partial chunks, which will raise the ValueError that you see when decrypted.

A second problem is that the encrypted files are being opened in text mode. When dealing with encrypted data open files in binary mode. Encrypted bytes are unlikely to be decodable to str, so using text mode will result in encoding or decoding errors.

This version of your code works:

import functools

from Crypto.PublicKey import RSA 
from Crypto.Cipher import PKCS1_OAEP

if __name__ == '__main__':

    # Encrypt

    with open('public.pem', 'r') as fp: 
        pub = fp.read()
        fp.close()

    public = RSA.importKey(pub)

    # tockage du fichier dans une variable rep
    with open('test.csv', 'r') as fichier:
        rep = fichier.read()

    # liminations des spaces
    rep = rep.replace(' ', '') 

    # ncodage pour type bytes
    rep = rep.encode()

    cipher = PKCS1_OAEP.new(public)

    # decoupage en mot de 10 chars
    rep = [rep[i:i+10] for i in range(0, len(rep), 10)]

    # Open the file in binary mode so we can write bytes.
    with open('encrypted.csv', 'wb') as fichier2:
        for i in rep:
            fichier2.write(cipher.encrypt(i))

    # Decrypt
    with open('private.pem', 'r') as fk: 
        priv = fk.read()

    private = RSA.importKey(priv)

    CHUNK_SIZE = 256 
    # Open the file in binary mode so we can read bytes.
    with open('encrypted.csv', 'rb') as fichier:
        # Create an iterator that will return chunks of the correct size.
        chunker = iter(functools.partial(fichier.read, CHUNK_SIZE), b'')
        rep = list(chunker)

    cipher = PKCS1_OAEP.new(private)

    with open('decrypted.csv', 'w') as fichier2:
        for i in rep:
            decrypted_line = cipher.decrypt(i)
            fichier2.write(decrypted_line.decode())

Rather than splitting the encrypted data on newlines, this code reads from the file in chunks of 256 bytes, as the encryption process seems to generate 256 bytes for each chunk of input. I'm not a cryptologist, so it's possible this isn't always true. In that case it might be better to encrypt (or decrypt) all the data in a single step.

Upvotes: 1

Related Questions