user4068802
user4068802

Reputation:

How to convert a key string back into a pycrypto key object? (Python)

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

Answers (2)

user4068802
user4068802

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

GeekInDisguise
GeekInDisguise

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

Related Questions