O'Reilly Hacks
oreilly.comO'Reilly NetworkSafari BookshelfConferences Sign In/My Account | View Cart   
Book List Learning Lab PDFs O'Reilly Gear Newsletters Press Room Jobs  


 
Buy the book!
IRC Hacks
By Paul Mutton
July 2004
More Info

HACK
#83
Encrypt Messages
Privacy is very important to some people. The only way to ensure that other people cannot read your messages is to encrypt them
The Code
[Discuss (0) | Link to this hack]

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.


O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.