10 : Secure Communication

There is a faint smile on his face. He clicks, the smile turns to panic. He involuntarily speaks out, “Oh, my god”. Your CEO has just forwarded an email to Kiran, a union leader, instead of Kiran, the CFO. You manage to save the day. The recipient is on leave and you delete the mail from his inward queue.

Your HR chief is heading towards you. He hurriedly asks you to check your email. You are surprised and smile. The CEO has declared a holiday for his birthday. The smile doesn't last long. The HR chief is asking you to find out who sent that email as it wasn't the CEO. You are reminded of the line routinely written by banks on their statements - “This is a computer generated statement and does not require a signature”!

Increasingly, your financial dealings are predominantly online. The statements are sent by email. To minimise the chances of a wrong person seeing your personal information, the statements are password protected. The passwords aren't very strong. They protect against casual snooping, which is fine for most of us. The irritation is that you need to spend effort to figure out the password for each statement. It's not easy, at least at my age.

There has to be a better way.

Public Key Infrastructure

Public key based algorithms have been around for about as long as I have been in the software field. Ubuntu owes its existence to the money made from the sale of Thawte, which issues digital certificates.

PGP(Pretty Good Privacy) came into existence in early 1990's and GPG(Gnu Privacy Guard) conforming to the OpenPGP standard was available by the end of 90's.

I have used a public key only for starting a ssh session on a remote computer without having to give a password. But I have relied on gpg whenever I installed packages from a Fedora repository. Keeping the private key safe is a critical part of these security processes. The fear that the signing key may have been compromised resulted in the closure of the Fedora repositories for a noticeable period of time.

One reason for lack of applications using OpenPGP may be that it is hard to get started with them. It is important to realise that this technique is based on people trusting each other and not on a 3rd party certificate. Would I trust the keys more had the issuing company been audited by, say, PWC? A transaction between two parties does not need a certificate from a third party.

Before getting into programming using gpg, let us consider the steps involved in using the public key infrastructure using an email client, Evolution. We choose evolution as it comes with gpg support. Many email clients now support OpenPGP, e.g. Sylpheed. Thunderbird requires the Enigmail plugin, which, unfortunately, was not compatible with the x64 application I was using. The default security mechanism of Thunderbird is S/MIME. See http://www.mozilla-enigmail.org/forum/viewtopic.php?f=7&t=67 for more details.

GPG and Email

The first step is to create your own pair of keys for your email account, user@example.com. It is simple. Just give the following command:

$gpg –gen-key

You will be asked a few questions and if in doubt, just use the defaults. It is better if you give a pass phrase to protect your private key, especially if others may have access to the system you are using.

You will need to send your public key to your collaborators. So, export it as a text file and email it:

$gpg -a –export user@example.com > my_public_key.asc

Your friends can call you and verify that the fingerprint of the key is valid. You can find out the finger print by the command:

$gpg –fingerprint

You can now sign and send an email to your friends. In Evolution, choose the option 'Security' on the menu bar. Select the 'PGP Sign' check box.

You will get the public key from your friends and collaborators. You will need to import them. The step is again simple.

$gpg –import his_public_key.asc

GPG expects each key to be signed by a trusted party before it is regarded as valid. So, you will need to sign the key you have just imported as follows - assuming that his email address is friend@example.com:

$gpg –sign-key friend@example.com

Now, you can encrypt the email you are sending to your friend. When composing an email, choose the 'Security' option from the menu bar and select the “PGP Encrypt” check box. You can encrypt and sign the email by selecting both the sign and the encrypt check boxes.

If you have received an encrypted and signed email from your friend, Evolution will display it as usual, except that there will be a message at the bottom of the email informing you that it had a valid signature and was encrypted.

If you forward the encrypted mail to someone else, including your friend, the recipient will not be able to decode the mail. This is very useful for sending email to a relation who loves to gossip and is an uncontrollable mailing list! A side effect is that unless you copy an encrypted mail to yourself, you can't see what you sent.

If you do not have the public key of a recipient and you give the request to encrypt the mail, Evolution will give you an error. However, if both Kiran's had a public key in the key ring, encryption is not going to prevent you from making a mistake.

An Example Application

Programs can make mistakes – consistently. They do not normally make silly mistakes unless, of course, programmed to do so. We may expect companion robots for the ageing societies to be so programmed so that humans can relate to them!

You would like to send a salary slip to each employee so that only he can see it. Every employee on joining creates his key pair and registers his public key with the company. The admin staff need not manage these keys securely! In fact, they can freely distribute the public key to anyone who needs it, e.g. the bank where a salary account is opened. <Parenthetically>By the way, the employee can be a 'she' and the 'his' can be 'her'. I find the use of she/her to be even more distracting and would rather a new pair of words be coined for political correctness - how about he/her?</Parenthetically>

Python has a module pygpgme, which is a wrapper for the gpgme, GPG Made Easy, library. It is installed on Fedora as yum needs it. It lacks one small thing – documentation!

The gpgme library is documented but seems to lack any tutorials or articles on how to get started with it.

The solution in such cases is to download the source. You can actually ignore the source code. Search for the test cases. That can act as an excellent starting point.

Encrypting/Decrypting a File

For your application, you need to be able to encrypt a file. So, try the following code:

import gpgme

infile = open('salary_slip.txt')

outfile = open('salary_slip.asc','w')

ctx = gpgme.Context()

ctx.armor = True

recipient_key = ctx.get_key('friend@example.com')

ctx.encrypt_sign([recipient_key], gpgme.ENCRYPT_ALWAYS_TRUST, infile, outfile)

outfile.close()

infile.close()

The code is pretty straight forward. You open the two files and obtain the gpg context. The armor option creates an ascii file rather than a binary one. You obtain the key by using the recipient's email address and encrypt and sign the file by passing a list of the keys. The second option tells that the keys should be trusted. You will be prompted for the pass phrase for signing in case you have specified one while creating your key.

The code for decrypting a file is even simpler.

import gpgme

infile = open('salary_slip2.asc')

outfile = open('salary_slip.out','w')

ctx = gpgme.Context()

sigs = ctx.decrypt_verify(infile, outfile)

outfile.close()

infile.close()

Gpgme will raise an exception in case decryption fails or signature is not valid. Decrypt and verify method will return a list of signatures. You may want to know some more information about the signatures.

Since there is only one key in your case, try the following code:

signing_key = ctx.get_key(sigs[0].fpr)

print signing_key.uids[0].name

print signing_key.uids[0].email

You get the key by using the fingerprint and then print the information you may need.

Suppose you just wanted to sign a text:

import gpgme

infile = open('salary_slip.txt')

outfile = open('salary_slip_signed.asc','w')

ctx = gpgme.Context()

ctx.armor = True

ctx.sign(infile, outfile, gpgme.SIG_MODE_CLEAR)

outfile.close()

infile.close()

You have chosen the clear sign mode so that the text is readable and the signature identifiable.

This will be enough for us. You can read the code in the tests sub-directory of the pygpgme source (http://pypi.python.org/packages/source/p/pygpgme/pygpgme-0.1.tar.gz) to learn more.

MIME and PGP

Now you are in a position to combine encryption with the email module so that the hard part is done by the application and the user can access secure information very conveniently. The format for a mime encrypted message is described in http://www.ietf.org/rfc/rfc3156.txt.

Start with the various modules which need to be imported:

import smtplib

import gpgme

from email import encoders

from email.mime.base import MIMEBase

from email.mime.multipart import MIMEMultipart

from StringIO import StringIO

StringIO is a file-like class for manipulating a string buffer. It is, essentially, a memory file.

You will need to create a multi-part mime formatted message with the attachment you wish to email (see http://docs.python.org/library/email-examples.html for more details). You can assume that you are attaching a pdf file:

def create_message(filename):

outer = MIMEMultipart()

fp = open(filename, 'rb')

msg = MIMEBase('application', 'pdf')

msg.set_payload(fp.read())

fp.close()

encoders.encode_base64(msg)

msg.add_header('Content-Disposition', 'attachment', filename=filename)

outer.attach(msg)

return outer.as_string()

You will next encrypt the message:

def encrypt_payload(in_msg, out_msg):

ctx = gpgme.Context()

ctx.armor = True

recipient_key = ctx.get_key('friend@example.com')

ctx.encrypt([recipient_key], gpgme.ENCRYPT_ALWAYS_TRUST, in_msg, out_msg)

Now, you will need create another multi-part mime message which has the encrypted content as the payload. The MIME body must consist of exactly two body parts, the first with content type "application/pgp-encrypted". This body contains the control information. The second part contains the encrypted content as an octet-stream.

def mime_pgp_message(fp):

outer = MIMEMultipart(_subtype='encrypted',protocol='application/pgp-encrypted')

outer['Subject'] = 'Attached Encrypted - 5'

outer['To'] = 'friend@example.com'

outer['From'] = 'user@example.com'

msg = MIMEBase('application', 'pgp-encrypted')

outer.attach(msg)

enc_part = MIMEBase('application','octet-stream', name='encrypted.asc')

fp.seek(0)

enc_part.set_payload(fp.read())

outer.attach(enc_part)

return outer.as_string()

Now, you are ready to send the message:

def send_message(sender, recipients, composed):

s = smtplib.SMTP()

s.connect()

s.sendmail(sender, recipients, composed)

s.close()

You would be calling the above routines as follows:

in_msg = StringIO(create_message('Open.pdf'))

out_msg = StringIO()

encrypt_payload(in_msg, out_msg)

composed = mime_pgp_message(out_msg)

send_message('user@example.com', ['friend@example.com'], composed)

Unfortunately, signing the document introduces one more level of complexity. Before encrypting the message, you would need to sign it. For this as well, a multi-part message with two parts in the body are required. So, the steps would become:

  1. Create the MIME message

  2. PGP Sign the MIME message

  3. Create a multi-part MIME message with protocol application/pgp-signature

  4. PGP Encrypt the signed MIME message

  5. Create a multi-part MIME message with protocol application/pgp-encrypted

  6. Send the message

Final Words

This article turned out to be much harder than I expected as I could not find any tutorials or simple documentation on using gpgme or mime/pgp-encrypted, whether for Python or any other language. In case anyone knows of any, I would love to hear about it.

It is a pity that banks force us to change our passwords every few months. We also need to ensure that our passwords are not the same at all sites. They also cannot be the same as ones used on the previous few occasions. In short, keeping track of passwords is one horrendous problem. Desktop tools which help use the appropriate password for each application or web site are a solution for the wrong problem.

A public key environment would make it much easier and, unambiguously, shift the task of preventing any leakage of passwords from the host sites to the user only. The critical advantage is that we need to protect only one private password. Last but not least, it will save us from making hundreds of enemies because we used our gmail password at a social networking site. Our correspondents have, thus, been inflicted with “I want to be your friend” spam.


<Prev>  <Next>

Comments