CSC 221: Computer Programming I
Fall 2009

HW6: Java Strings and Encryption

The use of codes (or ciphers) as a means of hiding the meaning of messages traces its roots to ancient history. The first known military use of codes was by Julius Caesar in 50 - 60 B.C. The Caesar cipher specified that each letter in the alphabet would be encoded using the letter three positions later in the alphabet. For example, 'a' would be encoded as 'd', 'b' would be encoded as 'e', 'c' would be encoded as 'f', and so on. The code wraps around at the end of the alphabet, so 'x', 'y' and 'z' would be encoded as 'a', 'b', and 'c', respectively. The complete mapping of letters is shown below.

Caesar cipher: abcdefghijklmnopqrstuvwxyz | | | | | | | | | | | | | V V V V V V V V V V V V V defghijklmnopqrstuvwxyzabc

Although the Caesar cipher was effective in its time (when very few people could read at all), its simple pattern of encoding letters seems pretty obvious today. However, it can be generalized to create more effective ciphers. The Caesar cipher is an example of a substitution cipher, a code in which one letter of the alphabet is substituted for another. They key "defghijklmnopqrstuvwxyzabc" defines the letter mapping used by the Caesar cipher. Using a different key, a completely different substitution cipher is defined. For example:

Mystery cipher: abcdefghijklmnopqrstuvwxyz | | | | | | | | | | | | | V V V V V V V V V V V V V qwertyuiopasdfghjklzxcvbnm

Since there are 26! (or roughly 4 x 1026) different arrangements of the 26 letters in the alphabet, there are 26! different keys and so 26! different substitution ciphers that can be defined.

PART 1: Substitution Ciphers (75%)

The Cipher class contains a partial implementation of a substitution cipher. It has two constructors: the default constructor initializes the cipher so that it implements the Caesar cipher; the alternate constructor has a parameter that allows the use to provide the key for an arbitrary substitution cipher. The character encode and decode methods can be used to encode and decode lower-case letters by shifting them three positions in the alphabet. Make the following modifications to the class to complete its implementation.

  1. Modify the character encode and decode methods so that they handle upper-case letters and non-letters as well. An upper-case letter should be encoded/decoded just like its lower-case equivalent, producing the corresponding upper-case letter. For example, 'A' should be encoded as 'D' using the Caesar cipher. Characters that are not letters should be left as is. For example, the encoding of a space or exclamation mark should be that same character unchanged.

    Hint: the isUpperCase and toUpperCase methods from the Character class should be of use here. Character.isUpperCase takes a character as a parameter and returns true or false, depending on whether that character is an upper-case letter. For example, Character.isUpperCase('A') would return true. Character.toUpperCase takes a character as a parameter and returns the upper-case version of that character (if it is a letter). For example, Character.toUpperCase('d') would return 'D'.

  2. Implement the string encode and decode methods. The string encode method should repeatedly call the character encode method on each character of its string, and return an encoded copy of that string. Similarly, the string decode method should repeatedly call the character decode method on each character of its string, and return a decoded copy of that string.

PART 2: Rotating Ciphers (25%)

While substitution ciphers are reasonably effective at encoding/decoding messages, they are susceptible to various attacks that make them unworthy for serious encryption. Not all letters are equally likely in text, so the relative frequency of characters in a coded message can provide clues for decoding. For example, 'e' is the most frequently used letter in English text. If the letter 'w' appeared most frequently in a coded message, then one might guess that the substitution cipher maps 'e' to 'w'.

One approach to counter this type of analysis is to add rotation to the cipher. When encoding/decoding a message, the first letter is encoded/decoded using the specified cipher key. However, after that encoding, the key is rotated so that the first character is moved to the end. For example, using the Caesar cipher key "defghijklmnopqrstuvwxyzabc", the letter 'a' would be encoded as 'd'. But, after this encoding the key would be rotated to be "efghijklmnopqrstuvwxyzabcd". Thus, if the next letter to be encoded was another 'a', it would get encoded as an 'e' this time. Make the following modifications to the Cipher class so that it has the option of rotating the key when encoding/decoding a message.

  1. Modify the string encode and decode methods so that each has an additional boolean parameter. When one of these methods is called with a false parameter, the method should encode/decode exactly as before. If the parameter is true, however, the method should rotate the cipher key after each character is encoded/decoded.

    Note: when each method is finished encoding/decoding, the cipher key should be unchanged from its original value (so that more messages might be encoded or decoded using the same substitution cipher). Consider saving a copy of the cipher key before the rotation begins, so that you can reset the key after the encoding/decoding is completed.

  2. Add a private method named rotate that performs the rotation of the key. This method should be called by both string encode and decode methods after a character has been processed.

    Hint: the following statement suffices to rotate a string named str:

    str = str.substring(1, str.length()) + str.substring(0, 1);

One you have completed your modifications, create a substitution cipher with the key "qwertyuiopasdfghjklzxcvbnm" and use it to decode the following message with rotation:

Ehhpcymwzprzqqu. Jig gkqwtzxonjky dgfqjll amb iqhjmuy!

If the decoding is readable, then you will known that your implementation works as desired.