P4: Simple Encryption/Decryption
P4: Simple Encryption/Decryption

 

Due Date: Wednesday, June 23, 2004, 07:59:59AM

Goal

In your fourth programming assignment, you will be writing a a short Java program to perform simple encryption and decryption on a text file message. Your program will read in a message file, encrypt it as described below and then read back in the encrypted file and perform a decryption.

 
Learning Objectives
  •  Exposure to processing text files
  •  Exposure to simple cryptography
  •  Familiarity with basic input and output
  •  Familiarity with creating new subclasses
  •  Familiarity with writing test cases
  •  Familiarity with while loops
  •  Mastery of the Web-CAT Grader
  • The Scenario

    Personal privacy is of utmost importance in the global networked world. One of the best tools to help people safeguard their personal information is the use of cryptography software. Modern cryptography algorithms requires a knowledge of advanced mathematics and programming. In this assignment you will gain a very brief introduction in the production of cryptography software.

    Input File

    The input file for this program will be named message.txt. However, the your program must be designed to allow it to easily work with other input filenames without requiring the code of the class to be changed. This implies that the message.txt filename must not be "hard wired" into the code without the possibility of being changed. Sample input files can be downloaded using the links below. The following assumptions regarding the input file apply. The input file consists of lines of text. It may be assumed that the shortest line of text in the file to be encrypted will be of length no less than four characters. The number of lines of text in the file is unknown. The end of file/data while statement processing loop pattern must be used. A trivial sample input file is shown below:

    abcdefghijklmnopqrstuvwxyz
    

    Sample Input Files

    Downloading Sample Input/Output Files
    To download a file, right-click on the link. Select "Save Target As..." or "Save Link As...", choose your BlueJ project directory, and save it.

     

    Encryption Process

    Below is the description of each step of the encryption process. Where necessary an example is given to clarify the process described in the step.

    1. Remove any leading or trailing whitespace from the line.
    2. Convert all letters in the string to UPPERCASE.
      Step 2 Input Line:		"abcdefghijklmnopqrstuvwxyz"
      Step 2 Output Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    3. Perform the following character substitutions:
      Original
      Substitution
      A
      @
      E
      =
      I
      !
      J
      ?
      O
      *
      P
      #
      R
      &
      S
      $
      T
      +
      V
      ^
      X
      %
      (space)
      _
    4. Move the first half of the string to be the last half. (Note: for lines of odd length the line must be divided such that the first half being moved contains one more character than the last half.)
      Example 1:
      Step 4 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      Step 4 Output Line:		"NOPQRSTUVWXYZABCDEFGHIJKLM"
      Example 2:
      Step 4 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXY"
      Step 4 Output Line:		"NOPQRSTUVWXYABCDEFGHIJKLM"
    5. Swap the first 2 characters of the line with the last two characters.
      Step 5 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      Step 5 Output Line:		"YZCDEFGHIJKLMNOPQRSTUVWXAB"
    6. Swap the two characters immediately to the left of the middle of the string with the two characters that immediately follow them.
      Example 1:
      Step 6 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      Step 6 Output Line:		"ABCDEFGHIJKNOLMPQRSTUVWXYZ"
      Example 2:
      Step 6 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXY"
      Step 6 Output Line:		"ABCDEFGHIJKNOLMPQRSTUVWXY"

    Below gives an example of applying all six steps sequentially to the alphabet string:

    Encryption Input Line:		"abcdefghijklmnopqrstuvwxyz"
    Encryption Output Line:		"LM#Q&$+U^W%@BYZCD=FGH!?KN*"

    The Intermediate File

    The encryption of the message file will be written to an intermediate file named "cipher.txt". As with the other filenames, the Cipher class must be designed to allow it to easily work with other intermediate filenames without requiring the code of the class to be changed. Since the intermediate output file will serve as input to the decryption step, no header file information must be written to the file. A sample intermediate file that corresponds to the above input file is shown below:

    LM#Q&$+U^W%@BYZCD=FGH!?KN*

    Decryption Process

    Below is the description of each step of the decryption process. Be aware that the cipher that is being used is not symmetric, (i.e., encrypting a message and then decrypting the encrypted text may not always yield the exact original message.) Where necessary an example is given to clarify the process described in the step.

    1. Remove any leading or trailing whitespace from the line.
    2. Swap the two characters immediately to the right of the middle of the string with the two characters that immediately precede them. (Note: for lines of odd length the line must be divided such that the first half contains one more character than the last half.)
      Example 1:
      Step 2 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      Step 2 Output Line:		"ABCDEFGHIJKNOLMPQRSTUVWXYZ"
      Example 2:
      Step 2 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXY"
      Step 2 Output Line:		"ABCDEFGHIJKNOLMPQRSTUVWXY"
    3. Swap the first 2 characters of the line with the last two characters.
      Step 3 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      Step 3 Output Line:		"YZCDEFGHIJKLMNOPQRSTUVWXAB"
    4. Move the first half of the string to be the last half. (Note: in this step for lines of odd length the line must be divided such that the first half contains one less character than the last half.)
      Example 1:
      Step 4 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      Step 4 Output Line:		"NOPQRSTUVWXYZABCDEFGHIJKLM"
      Example 2:
      Step 4 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXY"
      Step 4 Output Line:		"MNOPQRSTUVWXYABCDEFGHIJKL"
    5. Perform the following character substitutions:
      Original
      Substitution
      @
      A
      =
      E
      !
      I
      ?
      J
      *
      O
      #
      P
      &
      R
      $
      S
      +
      T
      ^
      V
      %
      X
      _
      (space)
      Step 5 Input Line:		"@BCD=FGH!?KLMN*#Q&$+U^W%YZ"
      Step 5 Output Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    6. Convert all letters in the string to lowercase.
      Step 6 Input Line:		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      Step 6 Output Line:		"abcdefghijklmnopqrstuvwxyz"

      Below gives an example of applying all six steps sequentially:

      Example 2:
      Encryption Input Line:		"LM#Q&$+U^W%@BYZCD=FGH!?KN*"
      Encryption Output Line:		"abcdefghijklmnopqrstuvwxyz"

      The Output File

      The output file is named "decrypt.txt". An output file, which corresponds to the above input and intermediate files, is shown below.

      abcdefghijklmnopqrstuvwxyz
      		
      The files for this encryption/decryption sample will be available on the course web site. You can use this sample (as well as others of your own devising) as an input source in your own test cases by creating a BufferedReader attached to the message.txt URL. Note, however, that it is not sufficient to do your testing only using this sample--you must create additional tests using your own input (see the hints on testing your solution below).

      Solution Requirements

      • You must provide a class called "Cipher" to serve as the main entry point for your solution.
      • Your Cipher class must provide a method with the following signature:
        public void encrypt( BufferedReader inStream,
                             PrintWriter    outStream )
        	throws Exception
        {
        	// ...
        }
      • The Cipher.encrypt() method must correctly translate all of the input characters from the provided message inStream, and produce the corresponding encrypted output on outStream.
      • Your Cipher class must also provide a method with the following signature:
        public void decrypt( BufferedReader inStream,
                             PrintWriter    outStream )
        	throws Exception
        {
        	// ...
        }
      • The Cipher.decrypt() method must correctly translate all of the input characters from the provided encrypted message inStream, and produce the corresponding decrypted output on outStream.
      • The Cipher.encrypt() and Cipher.decrypt() methods must not throw any exceptions, except for those that arise from calls sent to inStream.
      The Cipher class is only responsible for storing data for the cipher technique described in this project. Behavior for handling other cipher techniques must NOT be included in the Cipher class.

      Implementation Hints

      You are encouraged to design your solution piecemeal. Begin by identifying the major tasks that have to be done, and then add detail for each of those tasks. Your implementation should be developed in the same manner. A good starting point for this assignment is the stepwise implementation of the previous program. Apply a similar strategy to the decryption process for this program. Remember that by decomposing the problem you allow yourself to focus on solving a single part of the problem at any one time. You will find that this will greatly increase your odds of producing a correct solution.

      Review the brief tutorial on Files and Stream-based Input and Output. Make sure you understand the basic methods described in it.

      The most direct strategy for implementing a solution is to read from the input stream one character at a time, processing each character appropriately. Characters that represent a replacement characters can be substituted by their corresponding encrypted/decrypted, character and all other characters can be echoed unchanged to the destination.

      You may also wish to look carefully at the "one character at a time" file copying example in the stream I/O tutorial.

      Remembering a Sequence of Characters

      Another issue that may come up is how one can "remember" a sequence of characters, so that they can be used again later. One thing that you can do is append a character onto the end of an existing character string:

          String oldCharacters = "";
          ...
          int myChar = in.read();
          ...
          // To "save" characters that have been read before, "add" them
          // onto the end of a string:
          oldCharacters = oldCharacters + (char)myChar;
          ...
          // see what has been saved up
          System.out.println( oldCharacters );
          ...
          // Clear it out to start over
          oldCharacters = "";
      

      Note that both of these hints use the funny notation "(char)myChar". This is called a type cast, and instructs Java to treat the number stored in myChar as the code for a single character, rather than as a plain number. If we didn't do that, Java would add a human-readable decimal representation of the number stored in myChar to oldCharacters, rather than adding the character whose code is stored in myChar.

      Testing Hints

      When it comes to testing, remember to write one or more test cases for each method that your write in your solution. Preferably, you should write these tests before (or as) you write the method itself, rather than saving testing until your code works. As you work on larger and larger programs, it is important to build skills in convincing yourself that the parts you have already written work as you intend, even if the full solution has not been completed.

      Also, In addition to trying to think of various cases that your methods should add formatting to, also write test cases for scenarios where a method should not take action (or should signal an error condition, if that is the behavior intended).

      Finally, be sure to review the section on Reading from and Writing to Strings in the stream I/O tutorial. By using one string as an input stream, and then generating output in another string, it can be very easy to write short test cases. Consider the following test method (which assumes your text fixture includes a p4Cipher object created from your Cipher class):

          public void testEncryption()
          {
              try
              {
                  // create the streams needed
                  BufferedReader inStream  =
                      IOHelper.createBufferedReaderForString(
                              "abcdefghijklmnopqrstuvwxyz" );
                  StringWriter   result    = new StringWriter();
                  PrintWriter    outStream = new PrintWriter( result );
      
                  // run the method to get results
                  p4Cipher.encrypt( inStream, outStream );
      
                  inStream.close();
                  outStream.close();
      
                  // test that the result is what was expected
                  assertEquals( result.toString(),
                                      "LM#Q&$+U^W%@BYZCD=FGH!?KN*" );
              }
              catch ( Exception e )
              {
                  // If this happens, something went wrong; 
                  fail();  // treat as a failed test
              }
          }
      

      If you are clever, you can even create a "helper" method in your test class that takes two strings, the input string and the expected output string, and carries out the above test. That way, you can write many test cases, each of which is performed simply by calling your "helper" to do all the work. Even when writing test cases, it is a good idea to try to capture repeated code sequences in reusable pieces (placing them in their own methods, for example).

      Submit Your Solution

      Program submissions work just like lab submissions. On BlueJ's main menu, click Tools->Submit.... Click on "Browse...", double-click to open the "CS 1054 Programs" folder, and select Program 4. Click "OK". Click "Submit". Click on the link provided in the submission response in order to view the results of the automated phase of program grading.

      If no "Program 4" entry is visible on BlueJ's submission menu, then the Web-CAT Grader is not yet accepting submissions for this assignment. Wait for a message posted to the course web site that submissions are being accepted, and try again.

      If any errors, warnings or suggestions are indicated, you can fix them and resubmit. You are expected to fix all such issues in your code. You may resubmit as many times as you like, up until the deadline. Be careful as the due time approaches--if you submit just over the deadline, a late penalty will be assessed.

    Computer Science 1054, Introduction to Programming in Java
    D. Barnette