My task was to simulate 2 players playing a game of tic tac toe over a secure connection. By secure connection I mean, they had to get a certificate from a Certificate Authority, check its authenticity and extract a public key, with which they encoded data and sent to the other player. So If a certain nosy person decided to listen into the conversation he will not be able to decrypt the messages with a private key.
I coded a tic tac toe game using sockets and threads no problem. The main issue came in when I wanted to encode data and create a X509 certificate. A very commonly used library for certificate creation is called OpenSSL. Since I rather code on my windows machine, I was having a lot of trouble trying to install it. The alternative I looked at is called pyOpenSSL, it is python wrapper to call certain methods in OpenSSL using a few commands. This library was very limited and did not provide encryption and decryption. Which means I would have to import other libraries such as RSA. After looking helplessly I found a lovely thing called M2Crypto. The only problem with it is that It has very little documentation or sample code online on how to exactly execute certain things. I was mostly looking at Stack Overflow questions people made about problems. After a couple of days of struggling and messing around with code, I have done it.
I decided to create an easy readable manual on M2Crypto for people who decide to work on it. This will provide a set of commands which you might require to implement a secure connection.
M2Crypto
M2Crypto download:http://chandlerproject.org/Projects/MeTooCrypto#Downloads
To import M2Crypto into code:
import M2Crypto
Importing certain libraries:
from M2Crypto import RSA, EVP
Generating a Random RSA key pair (public and private):
key= RSA.gen_key(512, 65537)
512 is the size of the key
65537 encryption standard use it as a default
Saving the generated public and private key:
key.save_key ('Alice-private.pem', None)
key.save_pub_key ('Alice-public.pem')
None is a specified password for testing purposes there is none
The file can be created in a specific directory ‘/local/temp/Alice-private.pem’
Converting the RSA key into a PKey() which is stored in a certificate:
pkey = EVP.PKey()
pkey.assign_rsa(key)
To create a X509 certificate using M2Crypto:
# time for certificate to stay valid
cur_time = M2Crypto.ASN1.ASN1_UTCTIME()
cur_time.set_time(int(time.time()) - 60*60*24)
expire_time = M2Crypto.ASN1.ASN1_UTCTIME()
# Expire certs in 1 hour.
expire_time.set_time(int(time.time()) + 60 * 60 * 24)
# creating a certificate
cert = M2Crypto.X509.X509()
cert.set_pubkey(pkey)
cs_name = M2Crypto.X509.X509_Name()
cs_name.C = "US"
cs_name.CN = “8080”
cs_name.Email = "fake@foo.com"
cert.set_subject(cs_name)
cert.set_issuer_name(cs_name)
cert.set_not_before(cur_time)
cert.set_not_after(expire_time)
# self signing a certificate
cert.sign(pkey, md="sha256")
cert.save_pem("cert")
This will create a “cert” file in your directory which can be loaded later on. Or you can use the cert object to convert into a string to send it through a socket EG:
message = cert.as_pem()
print message
Printing the certificate as a pem will look something like this, which is stored in a cert file if you open it with notepad:
-----BEGIN CERTIFICATE-----
MIIBYzCCAQ0CADANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJVUzENMAsGA1UE
AxMEODA4MTEfMB0GCSqGSIb3DQEJARYQZmFrZUBob3RtYWlsLmNvbTAeFw0xMjAz
MjEyMTU0MTJaFw0xMjAzMjMyMTU0MTJaMD0xCzAJBgNVBAYTAlVTMQ0wCwYDVQQD
EwQ4MDgxMR8wHQYJKoZIhvcNAQkBFhBmYWtlQGhvdG1haWwuY29tMFwwDQYJKoZI
hvcNAQEBBQADSwAwSAJBANFx5xZ/4k5Rx2XeAHrFLkaswFa8DIak1JnEMB+epKei
KNKrGliibUYp74YDAMw9CpA1QPzFqYKz7/zTivteSSkCAwEAATANBgkqhkiG9w0B
AQsFAANBALFh3S7rNMN/mYcmJzQQfe/FEEpVc0esRbeL/hvwWZs1C4yj+MwpCgBi
P/zSzkglB3WHe2VZk5i7kKv1pN+qvdU=
-----END CERTIFICATE-----
Once you receive the certificate as a string the way to convert it to a certificate object is:
m2cert = M2Crypto.X509.load_cert_string(message)
you can print the certificate object as a more readable string with this line:
print m2cert.as_text()
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 0 (0x0)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, CN=8081/emailAddress=fake@hotmail.com
Validity
Not Before: Mar 21 22:16:06 2012 GMT
Not After : Mar 23 22:16:06 2012 GMT
Subject: C=US, CN=8081/emailAddress=fake@hotmail.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (512 bit)
Modulus (512 bit):
00:c6:02:10:81:5d:c5:a4:30:83:24:91:ce:78:86:
7b:47:ea:20:02:ab:07:bc:91:39:7d:c1:5b:2c:3a:
e3:9c:b6:6a:96:86:9c:7e:e4:c8:6c:a9:8c:b1:33:
c6:1c:20:8c:cd:05:3e:b1:4b:69:49:9a:6c:0b:14:
a9:52:25:8d:47
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
67:59:f6:04:a4:21:8b:8e:ff:f9:6a:79:56:13:b9:cb:84:24:
9e:cd:b0:d7:c0:76:4f:99:bc:0f:14:f5:76:2a:07:04:c6:dc:
be:56:c1:68:5b:35:4e:f2:c7:b4:5b:ac:c9:c3:d9:a1:c1:d5:
73:52:9b:e2:36:45:ba:9b:a4:02
To extract an RSA key which can be used for encoding you use this:
pkey= m2cert.get_pubkey()
rsa_key_pub = pkey.get_rsa()
to encrypt a message using the public key above:
crypto = rsa_key_pub.public_encrypt(message, M2Crypto.RSA.pkcs1_oaep_padding)
M2Crypto.RSA.pkcs1_oaep_padding this is a RSA encryption type, there are different ones, but you should stick to one if you do not want to bother having to extract the type from the RSA key.
to decrypt a message using the private key, which you saved earlier, you need to load it first:
rsa_key_pri= M2Crypto.RSA.load_key(‘Alice-public.pem’)
once loaded use the command to decrypt:
decyphered = rsa_key.private_decrypt(message, M2Crypto.RSA.pkcs1_oaep_padding)
If you have any of the following errors:
RSAError: data too large for modulus
Check to make sure the data you are encrypting does not exceed the formula:
(sizeofkey/8) - 11 bytes
If it is then you will have to cut the message into slices and encrypt it OR encrease the size of the RSAKey
512 - moderate secure
1024 - really secure
2048 - military security
The bigger size of the key, the longer it will take to generate it.
RSAError: no start line
You are using the wrong file name or format for example if you open the .pem file for one of your private or public keys the header will say something like this:
-----BEGIN RSA PRIVATE KEY-----
or
-----BEGIN PUBLIC KEY-----
you need to make sure the file you are using is correct and you are not trying to encrypt using a private key or vice versa
RSAError: oaep decoding error
This means that during the method decrypt, you have used the wrong private key. The error means that the decryption that you get from decrypting the message is not the same as original one.
Make sure you are using correct keys it is easy to mix them up