Previous Section  < Day Day Up >  Next Section

Hack 83 Encrypt Messages

figs/expert.gif figs/hack83.gif

Privacy is very important to some people. The only way to ensure that other people cannot read your messages is to encrypt them.

When you send private messages to another IRC user, those messages are vulnerable to snooping. The messages are being sent as plain text and can therefore be sniffed while they travel from your client to the IRC server and finally to the recipient. Don't forget that the IRC server can obviously see what your messages are—so be careful what you say on untrustworthy IRC servers.

Using DCC chat [Hack #69] solves the server trust problem. DCC chat establishes a connection straight to the recipient and messages are sent along this connection. This is obviously still vulnerable, as plain text messages can still be sniffed on the Internet.

The Internet sniffing issue can be solved by using SSL (Secure Sockets Layer) to connect to the IRC server. While this will prevent your housemates sniffing your messages, it won't prevent the IRC server from seeing what your messages are, as they are decrypted when they arrive. Nor will it prevent your messages being sniffed if the recipient is not using a secure connection. You may also want to take into account the fact that traffic between IRC servers on the same network may not be encrypted.

13.7.1 Key Encryption

The only way to ensure that your messages cannot be intercepted is to ensure that they are encrypted as soon as they leave your IRC client. Rather than using private and public key cryptography techniques, the simplest way is to use a shared pass phrase or key. This means that anyone who knows the key can decrypt the messages. You will obviously need to tell the recipient what the key is, but for obvious reasons, you shouldn't tell them over IRC!

This hack will show you how to use Java to make two simple bots that can send and receive encrypted messages to and from each other. The bots will use a Cipher object to encode and decode arrays of bytes. Both bots must be supplied with the same key, otherwise one won't be able to tell what the other is saying.

13.7.2 The Code

Create a file called CryptBot.java with the following imports and fields:

import org.jibble.pircbot.*;

import java.security.*;

import javax.crypto.*;

import javax.crypto.spec.*;



public class CryptBot extends PircBot {

    

    private Cipher cipher;

    private SecretKey key;

    private IvParameterSpec p;

Now define the constructor for CryptBot. If you want to use two of these bots on the same server, they will have to have different nicknames, so the first argument to the constructor will be for the bot's name. Each bot will need to be told what the pass phrase or key is, so the second argument keyString is used to take this. The first 24 bytes of the keyString will be used to make a DES-EDE ("triple-DES") key, so it is padded with zero bytes if it is too short.

The constructor will also initialize the instance fields used to encrypt and decrypt messages. Append this to the CryptBot class:

    public CryptBot(String name, String keyString) throws GeneralSecurityException {

        setName(name);

        

        byte[] keyBytes = new byte[24];

        int length = Math.min(keyBytes.length, keyString.length( ));

        System.arraycopy(keyString.getBytes( ), 0, keyBytes, 0, length);

        

        DESedeKeySpec spec = new DESedeKeySpec(keyBytes);

        SecretKeyFactory keyFactory = 

        SecretKeyFactory.getInstance("DESede");

        key = keyFactory.generateSecret(spec);

        cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");

        p = new IvParameterSpec(keyBytes);

    }

Now to make a simple method to encrypt or decrypt an array of bytes. The parameter encrypt is set to true if the array is to be encrypted; otherwise, it will try to decrypt the array. The resulting array is returned by the method. If the message could not be decrypted, the original array is returned. Append the following method to the CryptBot class:

    public byte[] crypt(byte[] input, boolean encrypt) {

        

        byte[] output = input;

        try {

            cipher.init(encrypt ? Cipher.ENCRYPT_MODE:Cipher.DECRYPT_MODE, key, p);

            output = cipher.doFinal(input);

        }

        catch (GeneralSecurityException e) {

            // Unable to encypt or decrypt. Leave the input as it was.

        }

        return output;

    }

The sendEncryptedMessage method will allow us to send an encrypted message from the bot. The key will be used to encrypt the message, allowing it to be read by anyone else who holds a copy of the key. The first parameter is used to specify the target, which can be a nickname or a channel. The second parameter specifies the message, which gets encrypted, and the resulting bytes are sent encoded as hexadecimal digits. Append this method to the CryptBot class:

    public void sendEncryptedMessage(String target, String message) {

        byte[] plainText = message.getBytes( );

        byte[] encrypted = crypt(plainText, true);

        

        StringBuffer buffer = new StringBuffer( );

        for (int i = 0; i < encrypted.length; i++) {

            String hex = Integer.toString(

                    (encrypted[i] & 0xff) + 0x100, 16).substring(1);

            buffer.append(hex);

        }

        

        System.out.println("Sending encrypted message: " + new String(encrypted));

        

        sendMessage(target, buffer.toString( ));

    }

Now you can add one final method to allow each bot to receive private messages. This method overrides the onPrivateMessage method in the PircBot abstract class. When private messages are received, the bot will try to decrypt them and print out the decrypted message. Append this method to the CryptBot class:

    public void onPrivateMessage(String sender, String login,

            String hostname, String message) {



        try {

            byte[] encrypted = new byte[message.length( ) / 2];

            for (int i = 0; i < message.length( ); i += 2) {

                String hex = message.substring(i, i + 2);

                encrypted[i / 2] = (byte) Integer.parseInt(hex, 16);

            }

            

            byte[] plainText = crypt(encrypted, false);

            message = new String(plainText);

            

            System.out.println("Plain text from " + sender + ": " + message);

        }

        catch (Exception e) {

            // Message was not in a suitable format.

        }

        

    }

    

}

Piecing together all of the preceding code, you end up with the complete CryptBot class. All you need to do now is write a main method to instantiate a couple of them and tell them to talk to each other.

Save this in a file called CryptBotMain.java:

public class CryptBotMain {

    

    public static void main(String[] args) throws Exception {

        

        String keyString = "my top secret key";

        

        CryptBot bot1 = new CryptBot("CryptBot1", keyString);

        CryptBot bot2 = new CryptBot("CryptBot2", keyString);

        

        bot1.connect("irc.freenode.net");

        bot2.connect("irc.freenode.net");

        

        bot1.sendEncryptedMessage("CryptBot2", "Hello");

        bot1.sendEncryptedMessage("CryptBot2", "freenode rocks");

        bot1.sendEncryptedMessage("CryptBot2", "This is a secret message!");

        

    }

    

}

Note that both bots are constructed with different nicknames so they can join the same server. Both bots are given the same keyString, so they will be able to decrypt messages from each other.

13.7.3 Running the Hack

Compile the hack with:

C:\java\CryptBot> javac -classpath .;pircbot.jar *.java

Run the hack with:

C:\java\CryptBot> java -classpath .;pircbot.jar CryptBotMain

13.7.4 The Results

After the bots have connected to the IRC server, the main method tells the first bot to send some private messages to the second bot. If all goes well, you should see the second bot correctly decrypting the messages from the first bot:

Sending encrypted message: Hello

Sending encrypted message: freenode rocks

Sending encrypted message: This is a secret message!

Encrypted text from CryptBot1: 48656c6c6f

Plain text received from CryptBot1: Hello

Encrypted text from CryptBot1: 667265656e6f646520726f636b73

Plain text received from CryptBot1: freenode rocks

Encrypted text from CryptBot1: 54686973206973206120736563726574206d65737361676521

Plain text received from CryptBot1: This is a secret message!

Now you can feel safe in the knowledge that nobody can read your messages as you send them through unfamiliar servers.

    Previous Section  < Day Day Up >  Next Section