Reputation:
I am replicating a TCP client-server secure handshake in python using the pycrypto module. When the "server" sends the public key over the socket, I am forced to convert the public key to a string. The "client" then receives the public key as a string, which cannot be encrypted according the pycrypto
module.
I get the error:
AttributeError: 'str' object has no attribute 'encrypt' in reference to enc_data = public_key.encrypt(secret_piece, 12) on the client side.
How do convert the string public_key
back to its original value when it was first generated by the RSA module?
Server Code:
def main():
host = '127.0.0.1'
port = 5000
s = socket.socket()
s.bind((host,port))
s.listen(1)
c, addr = s.accept()
print "Connection from: "+str(addr)
while True:
data = c.recv(1024)
if not data:
break
print "from connected user: "+str(data)
print "Start the SSL Handshake..."
a = raw_input('Press enter to generate the key pair. ')
key = RSA.generate(1024, random_generator)
public_key = key.publickey()
print "Key pair generated"
a = raw_input('Press enter to send public key to client ')
print "Sending key..."
if c.send(str(public_key)):
print "Public Key Sent"
print "Waiting for secret list..."
if c.recv(1024):
print "List received."
secret_list = c.recv(1024)
a = raw_input('Press enter to check the information from the list. ')
decrypted_info = key.decrypt(secret_list.enc_data)
match_or_not = SHA256.new(decrypted_info).digest() == secret_list.hash_value
if match_or_not:
print "Info Matches. Sending the ciphertext..."
info_to_be_encrypted = "It seems all secure. Let's talk!"
aes = AES.new(Random.get_random_bytes(16), AES.MODE_ECB)
cipher_text = aes.encrypt(info_to_be_encrypted)
if c.send(cipher_text):
print "Ciphertext sent."
Client Code
def main():
host = '127.0.0.1'
port = 5000
s = socket.socket()
s.connect((host,port))
message = raw_input("-> ")
while message != 'q':
s.send(message)
public_key = s.recv(1024)
print 'Received from server: '+str(public_key)
message = raw_input("->Press enter to verify the public key.")
print "Public Key verified!"
message = raw_input("-> Press enter to prepare the secret list.")
print "Client prepares the secret list."
secret_piece = Random.get_random_bytes(16)
enc_data = public_key.encrypt(secret_piece, 12)
hash_value = SHA256.new(secret_piece).digest()
L = [enc_data, hash_value]
print "List is ready."
message = raw_input("-> Press enter to send the list")
s.send(str(L))
print "List sent."
print "Waiting for ciphertext from the server..."
if s.recv(1024):
print "Ciphertext recieved."
cipher_text = s.recv(1024)
print "The encrypted message is: " + cipher_text
print "The decrypted message is: " + aes.decrypt(cipher_text)
s.close()
Upvotes: 2
Views: 10403
Reputation:
I solved this issue by instead by pickling the public_key
object using cpickle
module aka pickle.dumps(public_key)
. The pickled object is able to travel over the socket and be "unpickled" by the client on the other side!
Upvotes: 0
Reputation: 1567
Solution: exporting/importing keys using exportKey()
and importKey()
Why your conversion does not work
Converting the key to a string using str()
does not work, as you can see from the example below.
>>> k = RSA.generate(1024)
>>> pk = k.publickey()
>>> str(pk)
'<_RSAobj @0x10e518908 n(1024),e>'
As you can see from the example above, the function str()
just returns the string '<_RSAobj @0x10e518908 n(1024),e>'
which does not show the actual key.
Converting the key object into a string
A good way to convert the key into a string is to export it using a standard format, for example the well-known PEM format, as shown below.
>>> pkstring = pk.exportKey("PEM")
>>> pkstring
'-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4SD0YSMWYAU27mFVKsHgtKWzM\n9jfvs2Xl+zCQpAHNtvYWTo6mnyWTwH4lGn7ulYdGx5gAJj6OlWg+CKoHXqPOh6e4\nP8DM97dM9QfP8d7el2ZCz1+5oMd8iQo+WPTM1qa5TMj9rZMpwAnSrS490LW6ZpTL\n7fChg3APljnspQ/7nQIDAQAB\n-----END PUBLIC KEY-----'
Now, the string returned by pk.exportKey("PEM")
corresponds to the actual key.
Converting the string back to a key object
This is also very easy, you can just type:
>>> importedpk = RSA.importKey(pkstring)
Now you can use the encrypt
method with the importedpk
key which has been converted from the string pkstring
.
To prove you that importedpk
has been loaded correctly, just type the following two commands on the python interpreter.
pk.encrypt("Hello", 12)
importedpk.encrypt("Hello", 12)
They should return the same output. In particular, if you type
pk.encrypt("Hello", 12) == importedpk.encrypt("Hello", 12)
the result should be
True
How to fix your code
In server code, replace
if c.send(str(public_key)):
print "Public Key Sent"
with
if c.send(public_key.exportKey("PEM")):
print "Public Key Sent"
In client code, replace
public_key = s.recv(1024)
with
public_key = RSA.importKey(s.recv(1024))
Upvotes: 0