- first we would encrypt numbers now not letters or symbols the whole message can be considered as a single big number
- second compouters can perform operations millions of times faster like any mechanical device like the enigma machine of the Germans durin Worl War II
In this tutorial we will play with bits and bits operations. Not to the RAS operations yet that will be in the next tutorial.
As in the other Secret Code tutorial 2 classes will be provided. One for console mode with a main() method that shows/test the encoding/decoding algorithms and a GUI version that will allow to dynamically show the encoding/decoding operations using the console class to perform the coding.
In the real world all the operations are performed on bits but it is very difficult to display them in a visual form so a class CharAndBits is provided to display all bits in bytes as character '0' and '1'. This class provides method to display at the console or in the GUI the binary representation of every byte.
This class will be used in the next tutorials.
The CharAndBits.java class to print/display bytes as bits fields
/**
* A class for easy representation of all ASCII character on the console
* and on GUIs.
* This class contains the binary representation of the char it represents
* If the char is prinatble it returns its ASCII representation else it returns '.'
*/
public class CharAndBits implements Comparable<CharAndBits> {
// the character by itself
private char theChar;
// the char to display (will be '.' if not printable
private char toAscii;
// it's int value used for bitwise operation
private int intValue;
// its 8 bits representation as a String
private String binaryStr;
// its 8 bits representation as 8 char containing '0' or '1'
private char[] binaryChar;
// its 8 bits representation as 8 int containing 0 or 1
private int[] binaryInt;
/**
* Constructor that receives the char as parameter
*/
public CharAndBits(char theChar) {
// save it
this.theChar = theChar;
// test if it is printable if it is the case use it else use '.'
if(isPrintable())
toAscii = theChar;
else
toAscii = '.';
// get it's int value
intValue = theChar;
intValue &= 0xFF; // ok we just support the 255 Ascii characters
// convert to binary char and int
binaryChar = new char[8];
binaryInt = new int[8];
int temp = intValue;
for(int i = 7; i >= 0; i--) {
binaryInt[i] = temp & 1;
binaryChar[i] = (char) binaryInt[i];
binaryChar[i] += '0';
temp >>>= 1;
}
// and the whole String
binaryStr = new String(binaryChar);
}
/**
* return true of false depending if the char is printable (GUI or console)
*/
public boolean isPrintable() {
return !Character.isISOControl(theChar);
}
/**
* Returns the binary representation of this char
*/
public String toBinaryString() {
return binaryStr;
}
/**
* Get the printable version of this char
*/
public char getPrintableChar() {
return toAscii;
}
/**
* Returns the int value of this char
*/
public int getIntValue() {
return intValue;
}
/**
* Getter for the binary char
*/
public char[] getBinaryChar() {
return binaryChar;
}
/**
* To test 2 EasyCharacter for equality
*/
public boolean equals(CharAndBits other) {
return compareTo(other) == 0;
}
/**
* Used to sort an array of EasyCharacter
*/
public int compareTo(CharAndBits other) {
return intValue - other.intValue;
}
/**
* A static method to get an array of EasyCharacter from a String
* (so the caller does not have to perform the loop himself)
*/
public static CharAndBits[] newCharAndBitsArray(String str) {
// if str is null return a 0 length array of EasyCharacter
if(str == null)
return new CharAndBits[0];
// convert String received as parameter as an array of char
char[] digit = str.toCharArray();
CharAndBits[] array = new CharAndBits[digit.length];
for(int i = 0; i < digit.length; i++)
array[i] = new CharAndBits(digit[i]);
return array;
}
/**
* A static method to get a printable String from an array of EasyCharacter[]
* to display at the console or in a GUI
*/
public static String getMsgString(CharAndBits[] array) {
StringBuilder sb = new StringBuilder(array.length);
// copy our EasyCharacter into the buffer
for(CharAndBits ea : array)
sb.append(ea.toAscii); // printable version
// return the StringBuilder as a String
return sb.toString();
}
/**
* To perform the Xor between 2 arrays of EasyCharcter
* and return an String with the 2 arrays XORed
* The first parameter is the message, the second the key
* The array returned will have the size of the message
* if the key is smaller than the message a wrapAround will occur
*/
public static String xorArray(CharAndBits[] msg, CharAndBits[] key) {
// check for null or no length message
if(msg == null || msg.length == 0)
return ""; // return String of 0 length
// create the digit to hold the xored value
int msgLen = msg.length;
StringBuilder sb = new StringBuilder(msgLen);
// check for null or empty key in that case just return a copy of our message
if(key == null || key.length == 0) {
for(CharAndBits ea : msg)
sb.append(ea.theChar);
return sb.toString();
}
// get length of the key and create new array
int keyLen = key.length;
// loop to perform the XOR between each element of the msg array and the key with wrap around
for(int i = 0; i < msgLen; i++) {
int val = msg[i].intValue ^ key[i % keyLen].intValue;
sb.append((char) val);
}
// convert the StringBuilder array to a String
return sb.toString();
}
/**
* static method that return a String representation in binary of an array of char
*/
public static String toBinaryString(char[] digit) {
// use a StringBuilder to append the binary represenation
StringBuilder sb = new StringBuilder(digit.length * 9); // * 9 for the blank space
for(char c : digit) {
CharAndBits ea = new CharAndBits(c);
// append the 01010101010
sb.append(ea.toBinaryString());
sb.append(' ');
}
// return as a String
return sb.toString();
}
}
Now the main class that can be run as a console application and will display the bits swap and rotation:
SwapAndRotate.java
import java.util.Scanner;
/**
* Secret Code V
*
* With the the invention on digital computers the coding/decoding took a completly new turn
* for two reasons:
* - first the processing of data could go thousand if not million of times faster than with
* mechanical coding machines like the Enigma machine
* - second the encription could be performed on numbers rather than on letters, numbers and even symbols
*
* Ok so what's so special with characters on a computer ? It is because characters are represented by
* a serie of bits. (We will stay with plain Ascii for now for simplicity).
*
* Imagine the message "Hello". The Ascii representation of Hello in 8 bits bytes is
* H e l l o
* 01001000 01100101 01101100 01101100 01101111
*
* Now we can imagine some simple encoding like:
* - swap bit 7 and 0 of first letter
* - swap bit 6 and 1 of second letter
* - swap bit 5 and 2 of third letter
* - ...
*
* or we can imagine an algortihm that will be performed on the whole message like rotating
* the whole messages bits by the number of characters in the message.
*
* These will be simple substituation that ressembles a lot at the Vigenere square
* (Secret Code II - Tutorial) and can be decrypted the same way by frequency analysis
*
* This tutorial will explore quite quickly these 2 techniques. The next tutorial Secret Code VI
* will explore how to use XOR operations and will approach more the RSA encryption algorithm.
*
* We will have a class PlayingWithBits that will support 3 algorithms
* - algorithm SWAP will swap 2 bits in each character of the message
* - algotithm ROTATE will rotate the bits in the whole message by a certain number of bits
* - algorithm BOTH will use both of the mechanisms
*
* The key for algorithm SWAP is a String of even length containing pair of numbers betwwen 0 and 7
* specifying which bits to swap in each character. If the message is longer than the number
* of pairs we will wrap around
* Example of key: "263704"
* - in the first character we will swap bits 2 and 6
* - in the second character we will swap bits 3 and 7
* - in the third character we will swap bits 0 and 4
* - in the fourth character we start over again so swap bits 2 and 6
*
* The key for algorithm ROTATE is a list of int separated by , that will specify by how many
* bits we will rotate (left or right) the whole message depending of the length of the message.
* Again there will be a wrap around if the message contains more character than number of rotate
* Example of key "4,-23,93,12"
* - if the message has 1 character we will rotate the message by 4 bits
* - if the message has 2 characters we will rotate the message by 23 bits to the left
* - if the message has 3 characters we will rotate the message by 93 bits
* - if the message has 4 characters we will rotate the message by 12 bits
* - if the message has 5 characters wrap around and we will rotate the message by 4 bits
*
*/
public class SwapAndRotate {
public static final int SWAP = 1, ROTATE = 2, BOTH = 3;
// the keys used for encript/decript
private int[] keySwap1, keySwap2; // swap first with second
private int[] keyRotate;
/**
* Constructor that receives the keys as parameter
*/
public SwapAndRotate(String swap, String rotate) {
// call common method to set the initial keys or change it
setKeySwap(swap);
setKeyRotate(rotate);
}
/**
* Method to set the original SWAP key and permit to change it on the fly
* If the String is invalid we set the key to length 0 so no swap will occur)
*/
public void setKeySwap(String key) {
// avoid invalid key
if(!bValidateSwapKey(key)) {
keySwap1 = new int[0];
return;
}
// decompose te key as an array of digit
int len = key.length();
char[] digit = key.toCharArray();
// so all digits are valid build our array
keySwap1 = new int[len / 2];
keySwap2 = new int[len / 2];
int k = 0; // index in both arrays
for(int i = 0; i < len; i += 2) {
// store and convert to int
keySwap1[k] = digit[i] - '0'; // swap that bit
keySwap2[k] = digit[i+1] - '0'; // with that one
// the bit representation in the array are
// char[0]-bit 7 char[1]-bit 6 ... char[7]-bit 0
// so reverse them
keySwap1[k] = 7 - keySwap1[k];
keySwap2[k] = 7 - keySwap2[k];
k++;
}
}
/**
* A public static method to validate the Swap key
* It can be used by an application that the key is valid before
* creating a SwapAndRotate object or before changing the key
*/
public static boolean bValidateSwapKey(String key) {
// avoid null key
if(key == null || key.length() == 0) {
return false;
}
// validate the key it must contain an even number of digits
int len = key.length();
if(len % 2 != 0) {
return false;
}
// all the digits in the key must be between 0 and 7
char[] digit = key.toCharArray();
for(char c : digit) {
if(c < '0' || c > '7') { // if invalid char
return false;
}
}
// so all digits are valid
return true;
}
/**
* To set the initial value or change the ROTATE key
* If the key string is invalid we will set the key length to 0 so no swp will occur
*/
public void setKeyRotate(String key) {
// avoid invalid key
if(!bValidateRotateKey(key)) {
keyRotate = new int[0]; // empty key
return;
}
// separate between each ,
String[] str = key.split(",");
int len = str.length;
// prepare the keyRotate array
keyRotate = new int[len];
// translate into int the Strings we know they are valid
for(int i = 0; i < len; i++)
keyRotate[i] = Integer.parseInt(str[i].trim());
}
/**
* A public static method to validate the Rotate key
* It can be used by an application that the key is valid before
* creating a SwapAndRotate object or before changing the key
*/
public static boolean bValidateRotateKey(String key) {
// avoid null key
if(key == null || key.length() == 0) {
return false;
}
// separate between each ,
String[] str = key.split(",");
int len = str.length;
// translate into int the String if one fail we set to 0 key and no translation will occur
for(int i = 0; i < len; i++) {
// try to convert next token
try {
Integer.parseInt(str[i].trim());
}
catch (NumberFormatException e) {
return false; // so the key is invalid
}
}
// ok valid key
return true;
}
/**
* Method that encode a message based on the registered keys
* the second parameter is the type of encryption SWAP, ROTATE or BOTH
*/
public char[] encode(String clear, int type) {
// validate that the message is not null or length == 0
if(clear == null || clear.length() == 0)
return new char[0];
// validate the type operation and if the key(s) is/are valid for that operation
switch(type) {
case SWAP:
// if no valid SWAP key return
if(keySwap1.length == 0)
return new char[0];
break;
case ROTATE:
// if no valid ROTATE key return
if(keyRotate.length == 0)
return new char[0];
break;
case BOTH:
// if any of the 2 keys not valid return
if(keySwap1.length == 0 || keyRotate.length == 0)
return new char[0];
break;
default: // invalid type of encoding
return new char[0];
}
// make an array of EasyCharacter from both the message
CharAndBits[] ea = CharAndBits.newCharAndBitsArray(clear);
// make the array of the char that will be returned
char[] digit = new char[clear.length()];
// perform the encoding
// if SWAP or BOTH start by the SWAP
if(type == SWAP || type == BOTH) {
performSwap(ea, digit);
// if BOTH rebuild the CharAndBits array
if(type == BOTH) {
for(int i = 0; i < ea.length; i++)
ea[i] = new CharAndBits(digit[i]);
}
}
// perform the rotate
if(type == ROTATE || type == BOTH) {
// determine wich rotate to use
int index = clear.length() % keyRotate.length;
int nbRotate = keyRotate[index];
// no need to rotate the message more than once
nbRotate %= ea.length;
performRotate(ea, digit, nbRotate);
}
return digit;
}
/**
* Method that encode a message based on the registered keys
* the second parameter is the type of encryption SWAP, ROTATE or BOTH
*/
public char[] decode(String coded, int type) {
// validate that the message is not null or length == 0
if(coded == null || coded.length() == 0)
return new char[0];
// validate the type operation and if the key(s) is/are valid for that operation
switch(type) {
case SWAP:
// if no valid SWAP key return
if(keySwap1.length == 0)
return new char[0];
break;
case ROTATE:
// if no valid ROTATE key return
if(keyRotate.length == 0)
return new char[0];
break;
case BOTH:
// if any of the 2 keys not valid return
if(keySwap1.length == 0 || keyRotate.length == 0)
return new char[0];
break;
default: // invalid type of encoding
return new char[0];
}
// make an array of EasyCharacter from both the message
CharAndBits[] ea = CharAndBits.newCharAndBitsArray(coded);
// make the array of the char that will be returned
char[] digit = new char[coded.length()];
// perform the encoding
// if ROTATE or BOTH start by the ROTATE
// perform the rotate
if(type == ROTATE || type == BOTH) {
// determine wich rotate to use
int index = coded.length() % keyRotate.length;
int nbRotate = keyRotate[index];
// no need to rotate the message more than once
nbRotate %= ea.length;
performRotate(ea, digit, -nbRotate);
// if BOTH rebuild the CharAndBits array before performing the swap
if(type == BOTH) {
for(int i = 0; i < ea.length; i++)
ea[i] = new CharAndBits(digit[i]);
}
}
// test if SWAP
if(type == SWAP || type == BOTH)
performSwap(ea, digit);
return digit;
}
/**
* Perform the encoding/decoding of the SWAP algorithm
* As only swapping is performed the same method can be used for
* encoding and decoding
*/
private void performSwap(CharAndBits[] ea, char[] digit) {
// pass through all our EasyCharacter
for(int i = 0; i < ea.length; i++) {
// get the bit to swap
int index = i % keySwap1.length;
int from = keySwap1[index];
int to = keySwap2[index];
// get the bit representation of our EasyCharacter
char[] zeroAndOne = ea[i].getBinaryChar();
// save first char
char tmp = zeroAndOne[from];
// put second at its place
zeroAndOne[from] = zeroAndOne[to];
// put back the saved one
zeroAndOne[to] = tmp;
// use the Integer parseInt() method with the base specified to read the binary digit
int newVal = Integer.parseInt(new String(zeroAndOne), 2);
// and store it in the encoded/decoded digit
digit[i] = (char) newVal;
}
}
/**
* Perform the rotate for encoding or decoding
* Encoding will rotate right decoding left
* the sign of number to shift received as parameter will tell me wich side to rotate
*/
private void performRotate(CharAndBits[] ea, char[] digit, int nbRotate) {
// build an array of char containing the bit representation of the whole message
// we are using a StringBuilder to perform this operation
StringBuilder sb = new StringBuilder(ea.length);
for(CharAndBits c : ea)
sb.append(c.toBinaryString());
// make the char[] array
char[] orig = sb.toString().toCharArray();
// create the destination array
char[] dest = new char[orig.length];
// encoding
if(nbRotate > 0) {
// were we will start to copy
int where = nbRotate;
// pass through all the bits represented by a char '0' or '1'
for(char c : orig) {
// wrap around
if(where >= dest.length)
where = 0;
// put the char at that position
dest[where++] = c;
}
}
else { // decoding
int from = -nbRotate; // + because nbRotate is negative
for(int i = 0; i < dest.length; i++) {
// wrap around
if(from >= dest.length)
from = 0;
// put the char at that position
dest[i] = orig[from++];
}
}
// now read back 8 bits by 8 bits the new bits rotated
int k = 0; // index in digit[] array
for(int i = 0; i < dest.length; i+= 8) {
String heightBits = new String(dest, i, 8); // build String of 8 char '0' or '1'
int value = Integer.parseInt(heightBits, 2);
digit[k++] = (char) value;
}
}
/**
* To test the class
*/
public static void main(String[] args) {
// -------------------- generic test ----------------------------------
// ----------- these lines can be removed in production ---------------
// the 2 keys
String swapKey = "021324";
String rotateKey = "-1,10,23,-14"; // will garanty a rotate of 2
// create our SwapAndRotate object
SwapAndRotate sar = new SwapAndRotate(swapKey, rotateKey);
String msg = "DreamInCode";
char[] digit, digitBack;
String decoded;
// SWAP test
// print the message and its binary representation
System.out.println("SWAP test Message: " + msg);
System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
// encode with Swap
digit = sar.encode(msg, SwapAndRotate.SWAP);
// print the swap version
System.out.println("Swap:");
System.out.println(CharAndBits.toBinaryString(digit));
// decode message back
digitBack = sar.decode(new String(digit), SwapAndRotate.SWAP);
// print back the decoded version in binary
System.out.println("Swap Back:");
System.out.println(CharAndBits.toBinaryString(digitBack));
// message back
decoded = new String(digitBack);
System.out.println("Decoded: \"" + decoded + "\" is it the same as \"" + msg + "\" ? : " + decoded.equals(msg));
System.out.println();
// ROTATE test
// print the message and its binary representation
System.out.println("ROTATE test Message: " + msg);
System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
// encode with Rotate
digit = sar.encode(msg, SwapAndRotate.ROTATE);
// print the Rotate version
System.out.println("Rotated:");
System.out.println(CharAndBits.toBinaryString(digit));
// decode message back
digitBack = sar.decode(new String(digit), SwapAndRotate.ROTATE);
// print back the decoded version in binary
System.out.println("Rotated Back:");
System.out.println(CharAndBits.toBinaryString(digitBack));
// message back
decoded = new String(digitBack);
System.out.println("Decoded: \"" + decoded + "\" is it the same as \"" + msg + "\" ? : " + decoded.equals(msg));
System.out.println();
// BOTH test
// print the message and its binary representation
System.out.println("BOTH test Message: " + msg);
System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
// encode with Rotate
digit = sar.encode(msg, SwapAndRotate.BOTH);
// print the Rotate version
System.out.println("Swapped & Rotated:");
System.out.println(CharAndBits.toBinaryString(digit));
// decode message back
digitBack = sar.decode(new String(digit), SwapAndRotate.BOTH);
// print back the decoded version in binary
System.out.println("Swap & Rotated Back:");
System.out.println(CharAndBits.toBinaryString(digitBack));
// message back
decoded = new String(digitBack);
System.out.println("Decoded: \"" + decoded + "\" is it the same as \"" + msg + "\" ? : " + decoded.equals(msg));
System.out.println();
// --------------- end of generic test --------------------------------
// user input
Scanner scan = new Scanner(System.in);
// prompt for the key
String keySwap;
System.out.println("Enter SWAP key composed of pairs of bit number from 0 to 7");
do {
System.out.print("Enter key like 123464: ");
keySwap = scan.nextLine();
} while (!SwapAndRotate.bValidateSwapKey(keySwap));
// prompt for the Rotate key
String keyRotate;
System.out.println("Enter ROTATE key composed of integer separated by ,");
do {
System.out.print("Enter key like 12,546,323: ");
keyRotate = scan.nextLine();
} while(!SwapAndRotate.bValidateRotateKey(keyRotate));
// create SwapAndRotate object
SwapAndRotate obj = new SwapAndRotate(keySwap, keyRotate);
// get message
System.out.print("Enter message to encode/decode:");
String message = scan.nextLine();
// test SWAP
// print message with bits
System.out.println("SWAP Original message is: " + message);
System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
char[] swapDigit = obj.encode(message, SwapAndRotate.SWAP);
System.out.println("After SWAP:");
System.out.println(CharAndBits.toBinaryString(swapDigit));
char[] swapBack = obj.decode(new String(swapDigit), SwapAndRotate.SWAP);
System.out.println("After decode:");
System.out.println(CharAndBits.toBinaryString(swapBack));
System.out.println("Decoded message: " + new String(swapBack));
System.out.println();
// test ROTATE
// print message with bits
System.out.println("ROTATE Original message is: " + message);
System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
char[] rotateDigit = obj.encode(message, SwapAndRotate.ROTATE);
System.out.println("After ROTATE:");
System.out.println(CharAndBits.toBinaryString(rotateDigit));
char[] rotateBack = obj.decode(new String(rotateDigit), SwapAndRotate.ROTATE);
System.out.println("After decode:");
System.out.println(CharAndBits.toBinaryString(rotateBack));
System.out.println("Decoded message: " + new String(rotateBack));
System.out.println();
// test BOTH
// print message with bits
System.out.println("BOTH Original message is: " + message);
System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
char[] bothDigit = obj.encode(message, SwapAndRotate.BOTH);
System.out.println("After BOTH:");
System.out.println(CharAndBits.toBinaryString(bothDigit));
char[] bothBack = obj.decode(new String(bothDigit), SwapAndRotate.BOTH);
System.out.println("After decode:");
System.out.println(CharAndBits.toBinaryString(bothBack));
System.out.println("Decoded message: " + new String(bothBack));
System.out.println();
}
}
And now the GUI application. Notice, when the ROTATE mechanism is used, how all the bit fields are updated when you dynamically change the key or the message to encode.
This GUI is also a nice tutorial on how to use JTable
SwapAndRotateGui.java
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
/**
* A GUI that use the SwapAndRotate class to encode/decode messages
* In this GUI we cannot prompt for a String to decode as the swapped and rotated bits
* in byte may generate characters that may (most probably) not be displayable
*
* The GUI display the binary form of each letter in the message
* their coded representaion for SWAP, ROTATE and BOTH
* The bottom of the frame shows if each key SWAP and ROTATE are valid
*/
public class SwapAndRotateGui extends JFrame {
private static final long serialVersionUID = 1L;
// the SwapAndRotate class to encode/decode
private SwapAndRotate war;
// the keys to use
private JTextField keySwapText, keyRotateText;
// The message to encode
private JTextField clearTextIn;
// The message saying if the keys are valid
private boolean keySwapValid, keyRotateValid;
private JLabel swapValidLabel, rotateValidLabel;
// the original message
private char[] msgChar = new char[0];
// the decoded messages
private char[] swapEncoded, rotateEncoded, bothEncoded;
// the encoded messages
private char[] swapDecoded, rotateDecoded, bothDecoded;
// The JTable shown in the CENTER region
private JTable table;
private MyModel myModel;
// Its panel
private JPanel centerPanel;
// it's headers
private static final String[] header = {"Orig msg", "Swappped crypted", "Rotated crypted", "Both crypted",
"Swap decripted", "Rotate decrypted", "Both decrypted"};
// mnemonic for more descriptive values in the AbstractModel
private static final int ORIG = 0, SWAPPED = 1, ROTATED = 2, BOTH = 3,
SWAP_BACK = 4, ROTATE_BACK = 5, BOTH_BACK = 6;
/**
* Constructor
*/
SwapAndRotateGui() {
super("SwapAndRotate encoding/decoding");
// we will use a BorderLayout to store our GUI component
setLayout(new BorderLayout());
// the SwapAndRotate object init the keys to ""
war = new SwapAndRotate("", "");
// the Listener when one of the key changes
DocumentListener dc = new KeyListener();
// The NORTH region will contain a JPanel where the keys and the message can be entererd
JPanel north = new JPanel(new GridLayout(7,1, 2, 2));
// the SWAP key
north.add(createCenteredLabel("The SWAP key composed of pairs of bits to swap like 071625"));
keySwapText = new JTextField(50);
keySwapText.getDocument().addDocumentListener(dc);
north.add(keySwapText);
// the ROTATE key
north.add(createCenteredLabel("The ROTATE key composed of numbers of bits to rotate like 12,-34,92,18"));
keyRotateText = new JTextField(50);
keyRotateText.getDocument().addDocumentListener(dc);
north.add(keyRotateText);
// the message
north.add(createCenteredLabel("Enter the message to decode here"));
clearTextIn = new JTextField(50);
clearTextIn.getDocument().addDocumentListener(new ClearListener());
north.add(clearTextIn);
// a gap
north.add(new JLabel(" "));
// add this panel to the top of the screen
add(north, BorderLayout.NORTH);
// in the CENTER region of the frame we will put a JTable containing all the
// encrypted/decripted bits
myModel = new MyModel();
table = new JTable(myModel);
centerPanel = new JPanel(new GridLayout(1,1));
centerPanel.add(new JScrollPane(table));
add(centerPanel, BorderLayout.CENTER);
// The SOUTH region contains 2 JLabel specifying if the keys are valid
JPanel south = new JPanel(new GridLayout(1, 2));
// create the 2 labels using our centered label factory
swapValidLabel = createCenteredLabel("");
rotateValidLabel = createCenteredLabel("");
// we make them opaque as their foreground color changes depending if
// the key is valid or not
swapValidLabel.setOpaque(true);
rotateValidLabel.setOpaque(true);
// put them in the panel
south.add(swapValidLabel);
south.add(rotateValidLabel);
updateValidLabel();
add(south, BorderLayout.SOUTH);
// standard operation to show the JFrame
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(30, 30, 900, 500);
setVisible(true);
}
/**
* A method to create a JLabel with foreground color Blue and with text centered
*/
private JLabel createCenteredLabel(String text) {
JLabel label = new JLabel(text);
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setForeground(Color.BLUE);
return label;
}
/**
* One of the 2 key has changed
*/
private void updateKeyString() {
String keyStr;
// update both keys from their JTextField
keyStr = keySwapText.getText();
// check if SWAP key is valid
keySwapValid = SwapAndRotate.bValidateSwapKey(keyStr);
war.setKeySwap(keyStr);
// check if ROTATE key is valid
keyStr = keyRotateText.getText();
keyRotateValid = SwapAndRotate.bValidateRotateKey(keyStr);
war.setKeyRotate(keyStr);
// update the JLabel that says if the key are valid
updateValidLabel();
// set the color of the JTextTextFields displaying the key in black if the key is valid
// in RED is the key is not valid
if(keySwapValid)
keySwapText.setForeground(Color.BLACK);
else
keySwapText.setForeground(Color.RED);
if(keyRotateValid)
keyRotateText.setForeground(Color.BLACK);
else
keyRotateText.setForeground(Color.RED);
// update the keys in the SwapAndRotateObject
// and update the string to encode
updateStringToEncode();
}
/**
* To update the string to be coded
*/
private void updateStringToEncode() {
// get the text from the JTextField
String line = clearTextIn.getText();
// make the char[] array out of it to be displayed in the JTable
msgChar = line.toCharArray();
// build the encrypted char[] for the 3 coding algorithms
swapEncoded = war.encode(line, SwapAndRotate.SWAP);
rotateEncoded = war.encode(line, SwapAndRotate.ROTATE);
bothEncoded = war.encode(line, SwapAndRotate.BOTH);
// build the decrypted char[] for the 3 coding algorithms
swapDecoded = war.decode(new String(swapEncoded), SwapAndRotate.SWAP);
rotateDecoded = war.decode(new String(rotateEncoded), SwapAndRotate.ROTATE);
bothDecoded = war.decode(new String(bothEncoded), SwapAndRotate.BOTH);
// inform the model that the table contain changed
myModel.fireTableDataChanged();
}
/**
* To update the labels saying if the keys are valid
*/
private static final Color darkGreen = new Color(0, 120, 0);
private void updateValidLabel() {
// updates the text of both labels saying if the key is valid
// update also the foregound and background colors
// valid: WHITE and GREEN
// no valid: YELLOW and RED
if(keySwapValid) {
swapValidLabel.setText("SWAP key is valid");
swapValidLabel.setForeground(darkGreen);
swapValidLabel.setBackground(Color.WHITE);
}
else {
swapValidLabel.setText("SWAP key is NOT valid");
swapValidLabel.setForeground(Color.RED);
swapValidLabel.setBackground(Color.YELLOW);
}
if(keyRotateValid) {
rotateValidLabel.setText("ROTATE key is valid");
rotateValidLabel.setForeground(darkGreen);
rotateValidLabel.setBackground(Color.WHITE);
}
else {
rotateValidLabel.setText("ROTATE key is NOT valid");
rotateValidLabel.setForeground(Color.RED);
rotateValidLabel.setBackground(Color.YELLOW);
}
}
/**
* To start the GUI
*/
public static void main(String[] args) {
new SwapAndRotateGui();
}
/**
* A listener to be informed whenever the JTextField of the clear text is changed
*/
private class ClearListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent arg0) {
updateStringToEncode();
}
@Override
public void insertUpdate(DocumentEvent arg0) {
updateStringToEncode();
}
@Override
public void removeUpdate(DocumentEvent arg0) {
updateStringToEncode();
}
}
/**
* A listener to be informed whenever the JTextField of the SWAP or ROTATE key is changed
*/
private class KeyListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent arg0) {
updateKeyString();
}
@Override
public void insertUpdate(DocumentEvent arg0) {
updateKeyString();
}
@Override
public void removeUpdate(DocumentEvent arg0) {
updateKeyString();
}
}
/**
* A class that extends AbstractTableModel to povide the binary representation
* of every cell of the JTable in the center panel
*/
private class MyModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
// the number of columns is the length of the header of these columns
public int getColumnCount() {
return header.length;
}
// name of each colum
public String getColumnName(int column) {
return header[column];
}
// return the row count
public int getRowCount() {
// return the length of the message in the JTextField
return clearTextIn.getText().length();
}
// the JTable want's to know what to print there
public Object getValueAt(int row, int col) {
// validate first the row
if(row >= msgChar.length)
return "";
// the CharAndBits to display
CharAndBits cab = null;
// the cell text
String str;
// depending of the column display the correct text field
switch(col) {
// the only one where the char preceedes the binary String
case ORIG:
cab = new CharAndBits(msgChar[row]);
str = cab.getPrintableChar() + " - " + cab.toBinaryString();
return str;
case SWAPPED:
if(row >= swapEncoded.length)
break;
cab = new CharAndBits(swapEncoded[row]);
break;
case ROTATED:
if(row >= rotateEncoded.length)
break;
cab = new CharAndBits(rotateEncoded[row]);
break;
case BOTH:
if(row >= bothEncoded.length)
break;
cab = new CharAndBits(bothEncoded[row]);
break;
case SWAP_BACK:
if(row >= swapDecoded.length)
break;
cab = new CharAndBits(swapDecoded[row]);
break;
case ROTATE_BACK:
if(row >= rotateDecoded.length)
break;
cab = new CharAndBits(rotateDecoded[row]);
break;
case BOTH_BACK:
if(row >= bothDecoded.length)
break;
cab = new CharAndBits(bothDecoded[row]);
break;
}
// common formatting the binary string followed by the printable version of the char
// unless not defined yet due to the way the GUI refresh the JTable
if(cab == null)
return "";
str = cab.toBinaryString() + " - " + cab.getPrintableChar();
return str;
}
}
}





MultiQuote


|