Error: Ciphertext length must be equal to key size

I’m trying to make an end-to-end encrypted messenger in Python. I’ve got STL/TLS to work, and now I’m working on end-to-end encryption. Here is my full error

decrypted_chunk = private_key.decrypt( File “/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/rsa.py”, line 422, in decrypt raise ValueError(“Ciphertext length must be equal to key size.”) ValueError: Ciphertext length must be equal to key size.`

I’ve tried to edit the code, changing the key size and the size of the sent data, but the error remains. Here is the code for my server (please note: my computer is currently acting as both server and client):

import socket
import ssl
import base64
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend

PORT = 9000

# Generate RSA key pair
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=4096
)

# Set up SSL context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile="server.crt", keyfile="server.key")

# Create TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    # Bind the socket to a specific address and port
    server_address = ("", PORT)
    sock.bind(server_address)

    # Listen for incoming connections
    sock.listen(1)

    while True:
        # Wait for a connection
        print("Waiting for a connection...")
        client_sock, client_address = sock.accept()

        print("Connection established from", client_address)

        # Wrap the socket with SSL
        ssl_sock = context.wrap_socket(client_sock, server_side=True)

        # Receive the public key from the client
        public_key_bytes = ssl_sock.recv(1024)
        public_key = serialization.load_pem_public_key(
            public_key_bytes,
            backend=default_backend()
        )

        # Receive data over SSL
        while True:
            # Receive encrypted data over SSL
            data = b""
            while True:
                recv_data = ssl_sock.recv(1024)
                data += recv_data
                if len(recv_data) < 1024:
                    break

            if not data:
                break

            # Decode the base64-encoded ciphertext
            decoded_data = base64.b64decode(data)

            # Decrypt the ciphertext using the private key
            decrypted_plaintext = b""
            for i in range(0, len(decoded_data), 256):
                chunk = decoded_data[i:i+256]
                decrypted_chunk = private_key.decrypt(
                    chunk,
                    padding.OAEP(
                        mgf=padding.MGF1(algorithm=hashes.SHA256()),
                        algorithm=hashes.SHA256(),
                        label=None
                    )
                )
                decrypted_plaintext += decrypted_chunk

            print(f"Received: {decrypted_plaintext.decode()}")

            # Send data over SSL
            message = input("Enter message to send: ")
            ssl_sock.send(message.encode())

except Exception as e:
    print(f"Error: {e}")

# Close the socket
sock.close()

And here is the code for my client:

import socket
import ssl
import base64
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend

PORT = 9000

# Set up SSL context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations(cafile="server.crt")

# Create TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    # Connect to the server
    server_address = ("192.168.0.10", PORT)
    sock.connect(server_address)

    # Wrap the socket with SSL
    ssl_sock = context.wrap_socket(sock, server_hostname="192.168.0.10")

    # Generate a key pair
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=4096
    )
    public_key = private_key.public_key()

    # Send the public key to the server
    public_key_bytes = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )
    ssl_sock.send(public_key_bytes)

    # Receive data over SSL
    while True:
        # Take input from the user
        message = input("Enter message to send: ")

        # Check if the user wants to exit
        if message.lower() == "exit":
            break

        # Encrypt plaintext using the public key
        ciphertext = b""
        for i in range(0, len(message), 256):
            chunk = message[i:i+256]
            encrypted_chunk = public_key.encrypt(
                chunk.encode(),
                padding.OAEP(
                    mgf=padding.MGF1(algorithm=hashes.SHA256()),
                    algorithm=hashes.SHA256(),
                    label=None
                )
            )
            ciphertext += encrypted_chunk
        
        # Print out the length of the ciphertext
        print(f"Ciphertext length: {len(ciphertext)}")

        # Encode the ciphertext with base64
        encoded_ciphertext = base64.b64encode(ciphertext)

        # Send encoded data over SSL
        ssl_sock.send(encoded_ciphertext)

        # Receive data over SSL
        data = ssl_sock.recv(1024)

        print(f"Received: {data.decode()}")

except Exception as e:
    print(f"Error: {e}")

# Close the socket
sock.close()

Thanks in advance for any help!

By using TLS you already have encrypted the communications.
Why do you need to layer more encryption?

I need to use end-to-end encryption because TLS only encrypts the communication between the client and the server. E2EE means that even if someone intercepts the data while it’s on the server, they can’t read it because it’s encrypted.

The bufsize in recv(bufsize) is the maximum size, the “recv” method may return fewer bytes (in the middle of the transmission), resulting in a partial message.

@elis.byberi Thanks for the response. How would you fix this?

You have to check if chunk is an empty byte string.

while True:
    chunk = ssl_sock.recv(1024)

    if not chunk:
        break

    data += chunk

Sorry missed you where connecting two TLS.

You are right you need to layer the encryption and the authentication.

I’ve not done much with SSL, but for plain TCP I’ve got a module that helps. Feel free to use it or raid it for ideas:

It wraps things like send() and recv() to loop until a desired condition occurs - like receiving the desired number of octets or receiving a null byte.