Python 3 - Socket Chat Encryption with PyCrypto gives UnicodeDecodeError - python

I am trying to setup a socket chat with encryption in Python 3, but when decoding UTF-8 it gives an error.
Here is the code:
Client:
from Crypto.Cipher import AES
from Crypto import Random
import socket, sys
host = 'localhost'
port = 5558
IV = Random.new().read(16)
c = AES.new('abcd1234efgh5678', AES.MODE_CFB, IV)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
data = 'hey'.encode('utf-8') # 1
data = c.encrypt(data) # 2
s.sendall(data)
Server:
from Crypto.Cipher import AES
from Crypto import Random
import socket, sys
host = ''
port = 5558
IV = Random.new().read(16)
c = AES.new('abcd1234efgh5678', AES.MODE_CFB, IV)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host,port))
s.listen(10)
sock, addr = s.accept()
data = sock.recv(512)
data = c.decrypt(data) # 1
data = data.decode('utf-8') # 2
print(data)
After running these programs the server gives this error:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa5 in position 0: invalid start byte
So, I tried changing 'utf-8' in the Server code to 'latin-1' and it instead kept printing different unicode characters each time the program was run. I then swapped the 2 lines marked with comments in both the client and server, and of course it gave this error:
AttributeError: 'bytes' object has no attribute 'encode'
I have tried Google but all of the programs that use PyCrypto use Python 2, not 3. Eg. Encrypt & Decrypt using PyCrypto AES 256
http://eli.thegreenplace.net/2010/06/25/aes-encryption-of-files-in-python-with-pycrypto/

apart from the fact that your code above uses // as comments (should be #), i ran the code below (removed everything socket) and found the error: you re-initialize the IV before decrypting. this way you will not get the original value back - just some gibberish that may not be decodable in utf-8.
you will have to send the IV to the server (How to communicate AES initialization Vector to client for hybrid cryptosystem).
from Crypto.Cipher import AES
from Crypto import Random
# CLIENT -----------------------------------------
IV = Random.new().read(16)
c = AES.new('abcd1234efgh5678', AES.MODE_CFB, IV)
data = 'hey'.encode('utf-8') # 1
data = c.encrypt(data) # 2
# SERVER -----------------------------------------
# THIS IS WHERE YOUR CODE GOES WRONG!
# IV = Random.new().read(16)
c = AES.new('abcd1234efgh5678', AES.MODE_CFB, IV)
data = c.decrypt(data) # 1
data = data.decode('utf-8') # 2
print(data)

Related

How to encrypt string with AES and decrypt it in python with server and client in Python

I'm using Python 3.7.
My goal is to create a server that will encrypt a random string with a key (both the server & client have the "key"), in AES encryption. The server will send the ciphercode to the client, and the client will decrypt it.
However, the code I wrote doesn't show me any results. What I mean by that, is that the code is being "avoided" and I can't see any outcome from the code.
This is the code I wrote (server and client):
Server:
import socket
import Crypto
import Cryptodome
HOST = '127.0.0.1' # Standard loopback interface address (localhost)
PORT = 8080 # Port to listen on (non-privileged ports are > 1023)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
print ('Socket binded to port 8080')
s.listen(0)
print ('socket is listening')
while True:
c, addr = s.accept()
print ('Got connection from ', addr)
string = str(c.recv(1024))
print(string)
if string == "b'Register'" :
import random
import string
def randomString(stringLength=10):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
print("Random String is ", randomString())
from Crypto.Cipher import AES
from Crypto.Cipher import AES
def do_encrypt(message):
obj = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
ciphertext = obj.encrypt(message)
return ciphertext
The Client:
import socket
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 8080 # The port used by the server
import Crypto
import sys
sys.modules['Crypto'] = Crypto
name = '' # Sign in to the server!!
while name != 'Register':
print('Would you like to Register/Login?')
name = input()
s = socket.socket()
port = 8080
s.connect(('localhost', port))
z = 'Register'
s.sendall(z.encode())
s.close()
name = '' # Sign in to the server!!
print ("Register request sent")
from Crypto.Cipher import AES
def do_decrypt(ciphertext):
obj2 = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
message = obj2.decrypt(ciphertext)
return message
from Crypto.PublicKey import RSA
key = RSA.generate(2048) # Private key creation
private_key = key.export_key()
file_out = open("private.txt", "wb")
file_out.write(private_key)
public_key = key.publickey().export_key() # Public key creation
file_out = open("receiver.txt", "wb")
file_out.write(public_key)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # sending the Public key to the server.
s.connect((HOST, PORT))
s.sendall(public_key)
data = s.recv(1024)
s.close() # Close the socket when done
print('Received', repr(data))
Source of the AES encryption code: Sending Encrypted strings using socket in Python

Error 65537 while instatiating the CBC mode

I have been using the PyCryptoDome library to encrypt some communications between a client and a server. Here is how the protocol goes.
The server sends an RSA public key to the client.
The client generates an AES key
The client encrypts it. encrypts it using the public key (with the PKCS1_OAEP encrypt function)
The encrypted key is sent to the server.
The server decrypts the key
The server and client switch to AES-CBC to encrypt communications.
After that, when the client sends a message, the server decrypts it, uses it for what needs to be done with it, and then sends it back. All goes well until the client tries to decrypt the message that is sent back by the server. The thread for receiving messages stops, due to:
Error 65537 while instatiating the CBC mode
Different error codes happen when using different AES modes.
Here is my modified AESCipher Class:
import binascii
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher(object):
def __init__(self, key):
self.key = key
self.pad = lambda s: s + (AES.block_size - len(s) % AES.block_size) * \
chr(AES.block_size - len(s) % AES.block_size)
self.unpad = lambda s: s[:-ord(s[len(s) - 1:])]
def encrypt(self, raw):
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return binascii.b2a_hex(iv + cipher.encrypt(self.pad(raw)))
def decrypt(self, enc):
enc = binascii.a2b_hex(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self.unpad(cipher.decrypt(enc[AES.block_size:]))
Client receiving side (the error-causing side):
def recvMesgThread(netcat, output, aescipher):
while True:
try:
data = netcat.recv_until('\r\n').replace('\r\n', '')
except NetcatError:
print('Lost connection to server!')
sys.exit(0)
if data[:5] == '/MESG' and data[-5:] =='MESG/' :
try:
output.append(aescipher.decrypt(buf[5:-5]))
except Exception as e:
print(e)
Well, this is embarrassing...
The problem was a typo. Line 11 tries to decrypt the buf variable, which doesn't exist. The data variable was the one that contained what I needed.

Only byte strings can be passed to C code. python paramiko

I have a simple client-server program that uses python Paramiko.
The client sends an encrypted string to the server and the server needs to decrypt it to process it.
This is the client:
def collect_stats():
try:
cpu = psutil.cpu_percent(interval=1)
memory = psutil.virtual_memory().percent
disk = psutil.disk_usage('/').percent
str_to_send_back = "{} {} {}".format(cpu, memory, disk).strip()
str_to_send_back = str_to_send_back.encode()
str_to_send_back = encrypt(str_to_send_back)
print(str_to_send_back)
The server picks up the string and tries to decode it (at the bottom of this code snipper):
class server:
command='cd %temp% && cd' # Necessary to pull client temp directory path
run_client_script = 'cd %temp% && python client.py' # To run client script
def __init__(self, client_ip, cliet_port, username, password):
self.client_ip = client_ip
self.cliet_port = cliet_port
self.username = username
self.password = password
self.ssh = ssh(self.client_ip, self.username, self.password)
def upload_script_and_get_stats(self):
try:
# Built temp directory path for client machine
client_temp_dir_location = str(self.ssh.send_command(self.command)).strip()
# Client script file name
file_name = "\\client.py"
# Build absolute path of source file
source_file_location = os.path.dirname(os.path.abspath(__file__)) + file_name
# Build absolute path of destination file
destination_file_location = client_temp_dir_location + file_name
# Upload client script
ftp_client = self.ssh.open_sftp(source_file_location, destination_file_location)
# Run client script and get result
result = self.ssh.send_command(self.run_client_script)
# SERVER DECODES STRING HERE
result = self.decrypt(result)
return str(result)
except Exception as e:
print("Oops this error occured in transfer_files_to_client(): " + str(e))
finally:
self.ssh.close()
def decrypt(self, ciphertext):
try:
print(ciphertext)
obj2 = AES.new(b'This is a key123', AES.MODE_CFB, b'This is an IV456')
return obj2.decrypt(ciphertext)
except Exception as e:
print("FDSDSFDSF: "+str(e))
However, the decode method in server throws this error:
FDSDSFDSF: Only byte strings can be passed to C code
However what gets passed to the method is this:
b'\xb5\xf7\xbc\xd5\xfc\xff;\x83\xd3\xab\xb1mc\xc3'
which is already encoded.
Your ciphertext is a string that contains the string representation of a bytes object. I assume your server and client are running on Python 3 and so
print(str_to_send_back)
in the client just prints the representation of the bytes object to standard output. Try using some string friendly encoding for your bytes, such as base64:
from base64 import b64encode
def collect_stats():
try:
...
# str_to_send_back is actually bytes
# Decode the bytes that contain ASCII text for printing
print(b64encode(str_to_send_back).decode('ascii'))
and decode in the receiving end:
from base64 import b64decode
def decrypt(self, ciphertext):
# Removed the over eager "exception handling". The traceback is a
# lot more informative and useful. Add *specific* exception handling
# as necessary
aes = AES.new(b'This is a key123', AES.MODE_CFB, b'This is an IV456')
return aes.decrypt(b64decode(ciphertext))

socket error in python 2.7, sending public rsa key to client

I'm programming a client-server communication encrypted with RSA, using tcp sockets. I generate public and private keys, but when I want to exchange the public keys between client and server I get this error:
TypeError: must be convertible to a buffer, not PublicKey
This is server code:
import socket
import rsa
print "Generating keys"
(public, private) = rsa.newkeys(1024, poolsize=2)
print "Keys generated."
tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
tcpSocket.bind(("0.0.0.0", 1025))
tcpSocket.listen(1)
print "Waiting for client..."
(client, (ip, port)) = tcpSocket.accept()
print "Connection received from: ", ip
client.send(public) #This is where I get the error
I've tried with this line too:
client.send(str(public))
With this I can send the public key, but then I can't use it to encrypt data (because now the public key is a string).
Thank you for your help !
You can use Pickle or cPickle:
import cPickle
to_send=cPickle.dumps(the_key)
sock.send(to_send)
And then unpickle it:
import cPickle
s=sock.recv()
key=cPickle.loads(s)

Public key not recognized

I am trying to export a public key from openssl using python. I have the actual key information transferred to the client from the server but the PEM encoding is not transferred so the key on the client is useless. I basically send the public key using send all in python but unfortunately this does not send the PEM encoding. Does anyone know how to transfer the encoding?
I didn't know that the encoding would not transfer along with the key.
THe code where the string is read in
import socket
import M2Crypto as m2c
import os
max_transfer_block = 1024
server_addr = "10.1.1.2"
dest_port = 3333
listen_port = 8888
client_addr = "10.1.1.3"
mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysocket.connect((server_addr, dest_port))
#receive the public key from the server
keysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
keysocket.bind((client_addr, listen_port))
keysocket.listen(1)
conn, client_addr = keysocket.accept()
print 'connected by', client_addr
data = conn.recv(max_transfer_block)
#FILE = m2c.RSA.save_pub_key(data, "serverPubKey.pem")
FILE = open("sPub.pem", "w")
FILE.write(data)
keysocket.close()
#transfer encrypted file
key = m2c.RSA.load_pub_key('serverPubKey.pem')
FILE = open("test.txt", "r")
data = FILE.read()
encrypted = key.public_encrypt(data, m2c.RSA.pkcs1_padding)
mysocket.sendall(encrypted)
mysocket.close()
When I use the line key = m2c.RSA.load_pub_key('serverPubKey.pem') I get an error telling me that there is no starting point.
raise RSAError, m2.err_reason_error_string(m2.err_get_error()) M2Crypto.RSA.RSAError: no start line
I have figured out that this is because there is not in PEM format. Unfortunately, I don't know how to put it in that format.
The mistake was that the public/private key pair needs to be created from the same wrapper. What I mean by this is that not all key pairs are the same. My specific problem was that Openssl and the M2Crypto instances of key pairs were not in the same underlying format. Thus creating keys with Openssl and then trying to use M2Crypto to use the keys was wrong. The lesson from all of this is to not import keys from other wrappers. If you do, make sure that they are in the same underlying format like ASCII or Unicode before trying to use them.

Categories