Page 1 of 1

Secret Code VIII - Quadruple XOR An avenue to explore Rate Topic: -----

#1 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8347
  • View blog
  • Posts: 31,910
  • Joined: 06-March 08

Posted 29 September 2010 - 09:50 PM

The 7th tutorial about Secret Code
In Secret Code VI we saw how the XOR operator can be used to encrypt/decrypt messages.

This tutorial will show to what extent XOR can be used.

The class TwoXor.java just contains a main() method. It used the Xor.java class already post in tutorial Secret Code VI.

A GUI is also provided to see dynamically the changes when the message or the keys are changed.

The class CharAndBits.java coming from the previous tutorials is still used here. It has a little enhancement for console applications in the toBinaryString() methods to it will posted again. You can erase your old copy of this class without problem and used that new one or keep the old one.

Happy coding in both sens of the term.

TwoXor.java

/**
 * Secret Code VIII - Quadruple Xor
 * 
 * We continue here with the use of the Xor operator.  Actually we can do a lot more than we think
 * with this operator as we will see.
 * 
 * In tutorial Secret Code VI - The Xor operator we saw how two persons using the same key can exchange
 * crypted messages and decrypt them.
 * 
 * Now, how can two persons can exchange the key used ?  Before the invention of Arpnet and the Internet
 * it was easy to persons can physically exchange the key and that's it. But now, how can you exchange
 * a key with somebody in another continent ?
 *  
 * The big question became how to transmit a key to somebody else without having this message 
 * intercepted ?  Or is it possible to do it without exchanging keys ?
 * 
 * There is another trick an we will use an analogy.  Suppose Alice and Bernard want to exchange a
 * message (for a reason I ignore in all cryptology manuals they use Alice and Bernard and Eve as the
 * one who wants to intercept the message)
 * 
 * So Alice can put the message in a metal box, put a padlock on that box, keep the key of the padlock
 * and send the box by mail to Bernard. Even if Eve has friends at the post office, they cannot open
 * the box, so the box finishes by reaching Bernard's place.
 * 
 * Now, Bernard adds its own padlock to the box, keeps the key, and sends the box, still by mail, 
 * back to Alice.
 * 
 * When Alice receives the box, she removes her own padlock and sends back the box to Bernard.
 * 
 * Bernard receives the box, which is now only locked by his padlock, He takes his key, removes the
 * padlock, open the box and reads the message.
 * 
 * We can do that with the Xor operator. Lets write an example:
 * Alice's key:   11110000
 * Bernard's key: 00111100
 * Message sent by Alice: 10101010
 * 
 * Alice's msg:    10101010
 * Alice's key:    11110000
 *                 --------
 *   XOR           01011010  ----> sent to Bernard the message crypted (Xored) with Alice's key
 *   
 * Coded msg:      01011010  received my Bernard
 * Bernard's key:  00111100
 *                 --------
 *   XOR           01100110  ----> sent to Alice the message crypted (Xored) with Bernard's key
 *   
 * Twice coded:    01100110
 * Alice's key:    11110000
 *                 --------
 *   XOR           10010110  ---> sent back to Bernard the message decrypted (Xored) with Alice's key
 * 
 * half decoded:   10010110
 * Bernard's key:  00111100
 *                 --------
 *   XOR           10101010  ---> the original message back after 4 XOR
 *   
 * Amazing !!! The perfect encryption algorithm ? You thing you can defeat the NSA ?
 * Not really due to the Xor operation characteristic/features.
 * Imagine Eve can intercept the 3 messages that were sent over the network
 * These 3 messages are, as you surely remember:
 * Alice to Bernard: 01011010
 * Bernard to Alice: 01100110
 * Alice to Bernard: 10010110
 * 
 * Let's just Xor these messages for fun:
 * Msg 1: 01011010
 * Msg 2: 01100110
 *        --------
 *  XOR   00111100
 * Msg 3: 10010110
 *        --------
 *  XOR   10101010   <---- oups back to the original message
 * 
 * So whatever are the keys... you do not need them to decode the messages.
 * These sum of Xor are not suitable for a serious encryption algorithm. We will have
 * to consider other alternatives.  That will come in the next tutorial. 
 * 
 * Now this class just contains a main() method to prove the previous concept.
 * It use the classes Xor.java and CharAndBits.java already used an described in tutorial
 * Secret Code VI - The Xor operator.
 * (The new CharAndBits.java has improvments in its toBinaryStrings() methods to display the
 *  01010101 binary followed by _ displayable Ascii representation)
 */
public class TwoXor {

	/**
	 * To test the class double Xor
	 */
	public static void main(String[] args) {

		// original message
		String msg = "DreamInCode";
		String aliceKey = "Macosxnerd101";
		String bernardKey = "Dogstopper";
		
		// create the Xor objects of Alice and Bernard to encode/decode
		Xor xorAlice = new Xor(aliceKey);
		Xor xorBernard = new Xor(bernardKey);
		// for print out purpose only get the key used (it will be as long as the message)
		String aliceDupKey = xorAlice.dupKey(msg.length());
		String bernardDupKey = xorBernard.dupKey(msg.length());
		// the repeated Alice and Bernard keys in binary
		String aliceKeyInBin = CharAndBits.toBinaryString(aliceDupKey);
		String bernardKeyInBin = CharAndBits.toBinaryString(bernardDupKey);
		// build a series of dash -------
		char[] dash = new char[aliceKeyInBin.length()];
		for(int i = 0; i < aliceKeyInBin.length(); i++)
			dash[i] = '-';
		String dashStr = new String(dash);

		System.out.println("The original message is: \"" + msg + "\" the key used will be \"" + aliceDupKey + "\"");
		// call the utility method for binary representation of the message
		String msgInBin = CharAndBits.toBinaryString(msg);
		System.out.println(msgInBin);		
		System.out.println(aliceKeyInBin);
		// print the serie of ------
		System.out.println(dashStr);
		
		// encode the message for the first time which is the result of the XOR
		String encoded = xorAlice.encodeDecode(msg);
		// display the encoded bits
		String encodedInBinary = CharAndBits.toBinaryString(encoded); 
		System.out.println(encodedInBinary);
		
		System.out.println();

		// let's now Bernard apply the Bernard's key to the message
		// now the reverse process
		System.out.println("The first encoded message from Alice XORed with Bernard's key which is \"" + bernardDupKey + "\"");
		System.out.println(encodedInBinary);			// encoded message
		System.out.println(bernardKeyInBin);			// key in binary
		System.out.println(dashStr);                    // the ------------
		
		// encode the encoded message
		String decoded = xorBernard.encodeDecode(encoded);
		// display the encoded bits
		encodedInBinary = CharAndBits.toBinaryString(decoded); 
		// display the twice encoded message
		System.out.println(encodedInBinary);
		System.out.println();
		
		// now applying the Alice's key to the message
		System.out.println("The encoded message from Bernard to which we XOR Alice's key wich is \"" + aliceDupKey + "\"");
		System.out.println(encodedInBinary);
		System.out.println(aliceKeyInBin);
		// print the serie of ------
		System.out.println(dashStr);
		// now encode/decode again
		decoded = xorAlice.encodeDecode(decoded);
		// display the decoded bits
		encodedInBinary = CharAndBits.toBinaryString(decoded); 
		// display the twice encoded message
		System.out.println(encodedInBinary);
		System.out.println();
		
	    // now message back to Bernard
		System.out.println("Finaly Bernard applying again his key \"" + bernardDupKey + "\"");
		System.out.println(encodedInBinary);
		System.out.println(bernardKeyInBin);
		System.out.println(dashStr);
		// decoding
        decoded = xorBernard.encodeDecode(decoded);
		// display the decoded bits
		encodedInBinary = CharAndBits.toBinaryString(decoded); 
		System.out.println(encodedInBinary);		
		System.out.println("The final decoded message is \"" + decoded + "\" is it the same as \"" + msg + "\": " + msg.equals(decoded));

	}
	
}




Now the GUI part
TwoXorGui.java

import java.awt.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

/**
 * A GUI that use the Xor class to encode/decode messages two times to show
 * that by Xoring a first key, then a second key, the back with first key and
 * again second key we have the original message back
 * 
 * This is just to play around with quadruple Xoring
 * At the bottom of the JTable the way Eve, by Xoring the messages sent over
 * the internet can rebuild the original message
 */
public class TwoXorGui extends JFrame {

	private static final long serialVersionUID = 1L;

	// the Xor object to encode/decode one for Alice and one for Bernard
	private Xor xorAlice, xorBernard;

	// the keys to use
	private JTextField aliceKeyText, bernardKeyText;
	// the letters of the key for every letter in the key
	private char[] aliceLongKey, bernardLongKey;
	// The message to encode
	private JTextField clearTextIn;

	// the original message
	private char[] msgChar = new char[0];
	// the encoded messages
	private char[] firstMsgEncoded, secondMsgEncoded, thirdMsgEncoded;
	// the XOR of the messages
	private CharAndBits[] msg1XORmsg2, msg12XORmsg3; 
	// the decoded messages that should be the same as msgChar
	private char[] msgDecoded;

	// The JTable shown in the CENTER region
	private JTable table;
	private MyModel myModel;
	private TableColumnModel colModel;

	// Its panel
	private JPanel centerPanel;
    // the XOR String
    private static final String XOR = "   XOR ^";

	// the first column of the jTable
	private static final String[] firstCol = {"", "Alice Orig Msg", "Alice Key", "Encrypts", XOR, " ",
		"Bernard", "Bernard Key", "Encrypts", XOR, " ",
		"Alice", "Alice Key", "Decrypts", XOR, " ",
		"Bernard", "Bernard Key", "Decrypts", XOR, " ",
		"Eve's 1st catch", "Eve's 2nd catch", " ", " ", "Eve's 3rd catch", " ", "Orig Msg ?"};
	// mnemonic for more descriptive values in the AbstractModel where a switch() will be done
	private static final int ASCII = 0, 
							 ALICE1 = 1, ALICE_KEY1 = 2, SPACE1 = 3, XOR1 = 4, SPACE2 = 5,
							 BERNARD1 = 6, BERNARD_KEY1 = 7, SPACE3 = 8, XOR2 = 9, SPACE4 = 10,
							 ALICE2 = 11, ALICE_KEY2 = 12, SPACE5 = 13, XOR3 = 14, SPACE6 = 15,
							 BERNARD2 = 16, BERNARD_KEY2 = 17, SPACE7 = 18, XOR4 = 19, SPACE8 = 20,
							 LF1 = 21, LF2 = 22, LF3 = 23, LF4 = 24, LF5 = 25, LF6 = 26,
							 ORIGBACK = 27;

	/**
	 * Constructor
	 */
	TwoXorGui() {
		super("XOR encoding/decoding TWICE");
		// we will use a BorderLayout to store our GUI component
		setLayout(new BorderLayout());

		// the Xor object init the key to ""
		xorAlice = new Xor("");
		xorBernard = new Xor("");
		aliceLongKey = xorAlice.dupKey(0).toCharArray();
		bernardLongKey = xorBernard.dupKey(0).toCharArray();
		// the Listener 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 key of Alice
		north.add(createCenteredLabel("Enter Alice's Key"));
		aliceKeyText = new JTextField(50);
		aliceKeyText.getDocument().addDocumentListener(dc);
		north.add(aliceKeyText);
		// the key of Bernard
		north.add(createCenteredLabel("Enter Bernard's Key"));
		bernardKeyText = new JTextField(50);
		bernardKeyText.getDocument().addDocumentListener(dc);
		north.add(bernardKeyText);
		
		// the message
		north.add(createCenteredLabel("Enter Alice's message 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);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		colModel = table.getColumnModel();

		// the centerPanel will receive the JTable in a scrollPane
		centerPanel = new JPanel(new BorderLayout());
		centerPanel.add(new JScrollPane(table), BorderLayout.CENTER);
		add(centerPanel, BorderLayout.CENTER);

		// standard operation to show the JFrame
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(30, 30, 900, 700);
		setVisible(true);

		// to set the size of column 1
		updateStringToEncode();
	}

	/**
	 * 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 key has changed we could have two separated methods for
	 * each key but we will have to encode/decode all the messages in the path anyway 
	 */
	private void updateKeyString() {
		// update key in the Xor object from the key JTextField
		xorAlice.setKey(aliceKeyText.getText());
		xorBernard.setKey(bernardKeyText.getText());
		// update the encoding process
		updateStringToEncode();
	}

	/**
	 * To update the string to be coded
	 */
	private void updateStringToEncode() {
		// get the text of the message to encode from the JTextField
		String line = clearTextIn.getText();
		// make the char[] array out of it to be displayed in the JTable
		msgChar = line.toCharArray();
		// generate (for display purpose only) the key that will be used for every character
		aliceLongKey = xorAlice.dupKey(line.length()).toCharArray();
		bernardLongKey = xorBernard.dupKey(line.length()).toCharArray();
		// build the first encrypted message with Alice's key
		String encoded = xorAlice.encodeDecode(line);
		// in a char[] for display purpose
		firstMsgEncoded = encoded.toCharArray();
		// prepare the XOR of the messages intercepted by Eve
		CharAndBits[] msg1 = CharAndBits.newCharAndBitsArray(encoded);
		// build the encrypted messaage with Bernard's key
		encoded = xorBernard.encodeDecode(encoded);
		// in a char[]
		secondMsgEncoded = encoded.toCharArray();
		// prepare the XOR of the messages intercepted by Eve
		CharAndBits[] msg2 = CharAndBits.newCharAndBitsArray(encoded);
		// third translation Alice removes her key
		encoded = xorAlice.encodeDecode(encoded);
		// in a char[]
		thirdMsgEncoded = encoded.toCharArray();
		// prepare the XOR of the messages intercepted by Eve
		CharAndBits[] msg3 = CharAndBits.newCharAndBitsArray(encoded);
		// finally Bernard decoded the message
		encoded = xorBernard.encodeDecode(encoded);
		// in char[]
		msgDecoded = encoded.toCharArray();
		
		// Eve can now XOR the whole messages intercepted
		String tmp = CharAndBits.xorArray(msg1, msg2);
		msg1XORmsg2 = CharAndBits.newCharAndBitsArray(tmp);
		tmp = CharAndBits.xorArray(msg1XORmsg2, msg3);
		msg12XORmsg3 = CharAndBits.newCharAndBitsArray(tmp);
		
		// inform the model that the table contain changed 
		myModel.fireTableStructureChanged();
		myModel.fireTableDataChanged();
		// set the size of the column when a new column is added lets set it's size
		int actualColumnCount = (msgChar.length * 2) + 1;
		// no need to perform the columns width if one of the key is not defined
		if(aliceLongKey.length == 0 || bernardLongKey.length == 0)
			return;

		// set columns width
		for(int i = 0; i < actualColumnCount; i++) {
			TableColumn tc = colModel.getColumn(i); 
			tc.setPreferredWidth(100);
			tc.setMinWidth(100);
		}
	}

	/**
	 * To start the GUI
	 */
	public static void main(String[] args) {
		new TwoXorGui();
	}

	/**
	 * 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;
		// for ----- under the XOR
		private String dash = "--------------";
		// the String showing the message is transmitted
        private String arrow = "< received";
        
		// the number of columns is the length of the message + the first column
		public int getColumnCount() {
			// if any of the key is empty just show the first column
			if(aliceLongKey.length == 0 || bernardLongKey.length == 0)
				return 1;
			// else 2 columns for each letter of the message + the header column
			return  (msgChar.length * 2) + 1; 
		}

		// name of each colum (first one is empty)
		public String getColumnName(int column) {
			// if column 0 we return the hardcode "Steps"
			if(column == 0)
				return "Steps";
			
			// skip first column which is a header
			--column;
			
			// now for each char is the msg we have 2 column
			if(column / 2 >= msgChar.length)
				return "";

			// OK generate the title for the left coplumn or "transmitted" for the second
			if(column % 2 == 0)
				return "Digit #" + ((column / 2) + 1);
			else
				return "Transmitted";
		}

		// return the row count
		public int getRowCount() {
			// it is the length of our first column
			return firstCol.length;
		}

		// the JTable want's to know what to print there
		public Object getValueAt(int row, int col) {
			// for the first column we just return the header
			if(col == 0)
				return firstCol[row];
			// if no keys are entered no need to continue
			if(aliceLongKey.length == 0 || bernardLongKey.length == 0)
				return "";
			
			// get rid of the first column which shows headers
			--col;
			// validate first the col it should be contained in the mesage
			if((col / 2) >= msgChar.length)
				return "";
			
			// the CharAndBits to display init to null we will check it later
			CharAndBits cab = null;

			// check if left or right column
			boolean left = col % 2 == 0;
			// index in the messages
			col /= 2;
	
			// depending of the column display the correct text field
			switch(row) {
			    // first row the charac of the message and the 2 keys
				case ASCII:
					if(left)
						return "   Char: " + msgChar[col];
					else
						return " Keys: " + aliceLongKey[col] + "-" + bernardLongKey[col];
						
				// the encoded message from Alice
				case ALICE1:
					// the right column contains nothing
					if(!left)
						return "";
				    // left column the corresponding char in the original message
					cab = new CharAndBits(msgChar[col]);
					break;
					
	            // the binary representation of the Alice's key (same one at both row)
				case ALICE_KEY1:
				case ALICE_KEY2:
					// the right column
					if(!left)
						return "";
					// the left column
					cab = new CharAndBits(aliceLongKey[col]);
					break;		
									
				// Bernard first field
				case BERNARD1:
					// in the right column the String saying it received hat message
					if(!left)
						return arrow;
				case XOR1:				// which is Alice first encoding
					cab = new CharAndBits(firstMsgEncoded[col]);
					break;
					
				// Alice's second decoding where she removes her key
				case ALICE2:
					// right column show the message is received
					if(!left)
						return arrow;
				case XOR2:				// which is Alice second operation
					cab = new CharAndBits(secondMsgEncoded[col]);
					break;
		
				// Bernard second intervention. He decrypts the message
				case BERNARD2:
					// right column shows he received the message from Alice
					if(!left)
						return arrow;
				case XOR3:				// which is the 3rd message transmitted
					cab = new CharAndBits(thirdMsgEncoded[col]);
					break;
		
				case XOR4:				// final decode by Bernard
					if(!left)
						return " <--- Orig msg ?";
					cab = new CharAndBits(msgDecoded[col]);
					break;
					
					
	            // the binary representation of the Bernard's key (same one at both row)
				case BERNARD_KEY1:
				case BERNARD_KEY2:
					// the right column
					if(!left)
						return "";
					// the left column
					cab = new CharAndBits(bernardLongKey[col]);
					break;		
				
				// the space between each cluster
				case SPACE2:
				case SPACE4:
				case SPACE6:
				case SPACE8:
					return "";
					
				// the space before of the --------------
				case SPACE3:
					if(left)
						return dash;
					return " sent to Alice";
				case SPACE7:
					if(left)
						return dash;
					return "";
					
				case SPACE1:
				case SPACE5:
					if(left)
						return dash;
					return " sent to Bernard";
					
				// XOR of the 3 transmitted messages
				case LF1:
					if(left)
						return "  Msg #1";
					cab = new CharAndBits(firstMsgEncoded[col]);
					break;
				case LF2:
					if(left)
						return "  Msg #2";
					cab = new CharAndBits(secondMsgEncoded[col]);
					break;
				case LF3:
					if(left)
						return "";
					return dash;
				case LF4:
					if(left)
						return XOR;
					// XOR of the 2 first MSG
					cab = msg1XORmsg2[col];
					break;
				case LF5:
					if(left)
						return "  Msg #3";
					cab = new CharAndBits(thirdMsgEncoded[col]);
					break;
				case LF6:
					if(left)
						return "";
					return dash;
				case ORIGBACK:
					if(left)
						return XOR;
					//  MSG
					cab = msg12XORmsg3[col];
					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 "";
			return cab.toBinaryString();
		}

	}

}



Now the new version of CharAndBits.java that is used to display binary chars

/**
 * 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;
	}
	/**
	 * returns a printable version of an encoded String
	 */
	public static String toAsciiString(String encoded) {
		// convert the String to an array of CharAndBits
		CharAndBits[] array = newCharAndBitsArray(encoded);
		// prepare an array of char[] of the same length
		char[] digit = new char[encoded.length()];
		// get the printable version of every char
		for(int i = 0; i < encoded.length(); i++)
			digit[i] = array[i].toAscii;
		// return a String out of it
		return new String(digit);
	}
	/**
	 * 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 methods that return a String representation in binary of an array of char
	 */
	public static String toBinaryString(String str) {
		return toBinaryString(str.toCharArray());
	}
	public static String toBinaryString(char[] digit) {
		// use a StringBuilder to append the binary represenation
		StringBuilder sb = new StringBuilder(digit.length * 11);    // * 11 for -X the blank space
		for(char c : digit) {
			CharAndBits ea = new CharAndBits(c);
			// append the 01010101010
			sb.append(ea.toBinaryString());
			sb.append('_');
			sb.append(ea.toAscii);
			sb.append(' ');
		}
		// return as a String
		return sb.toString();
	}
}



Now if you are to lazy to get the Xor.java class from tutorial Secret Code VI here it is

import java.util.Scanner;

/**
 * Secret Code VI
 * 
 * If you have played with tutorial Secret Code V (SwapAndRotate) you will have seen as message can now
 * be considered as a big number instead of different digits or even symbols.
 * 
 * Before going to the complete RSA encoding/decoding system let us play with more basic encoding/decoding
 * mechanisms using binary code.
 * 
 * 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
 * 
 * In the Secret Code V tutorial we have seen how to swap and rotate bits in a message.
 * 
 * Now most encryptions rely on the bitwise operator XOR property that says that
 * if b and c are bits fields
 * a = b XOR c
 * a XOR c gives back b and
 * a XOR b gives back c
 * The XOR operator in Java is ^ and can be applied to integer.
 * 
 * Let us verify this assertion with all possible versions of 0 and 1
 * Message:           1100
 * Key:               1010
 *                    ----
 * XOR Msg and Key:   0110  this is the encrypted message
 * 
 * Now let's XOR the encrypted message with the key
 * Encrypted message: 0110
 * Key:               1010
 *                    ----
 * XOR                1100  back to the original message  
 * 
 * One big thing about this mechanism is that the process to encode is exactly the same
 * as the one to decode
 * we just XOR with the key both the message to encode and the message to decode.
 * No need for a Encode() method and a Decode() method. The same one is used
 * and the method does not need to know if it is actually encoding or decoding.
 * 
 * In this tutorial we will just play with this XOR feature to encode/decode messages
 * For that we will use a key if the key is smaller than the message we just repeat it
 * So the encode "Hello world" with the key "Dave" we will use as key
 *               "DaveDaveDav"
 * The following console application uses this technique
 * As in the previous tutorial we will used the CharAndBits class to output as a series of 0 and 1
 * the bits contained in a character.
 * 
 * If you have already the CharAndBits.java class/file from the previous tutotial, take this
 * new one, it has new functionnalities. The new version still support the code from
 * tutorial V so you can erase the old one, take the new one, and the code of SwapAndRotate will
 * still work.
 * 
 */
public class Xor {

	// the key used for encript/decript
    private String key;
    
    /**
     *  Constructor that receives the key as parameter
     */
	public Xor(String key) {
		// call common method to set the initial key or change it
		setKey(key);
	}
	
	/**
	 * Method to set the original key and permit to change it on the fly
	 */
	public void setKey(String key) {
		// avoid null key
		if(key == null)
			key = "";
		// save it
		this.key = key;
		
	}
	
	/** 
	 * Method that encode/decode a message based on the registered key
	 * Contrary to other coding mechanisms seen in the previous tutorials
	 * the mechanism to encode and decode is the same wo we do not need
	 * an encode and a decode method. The same method can be used for
	 * both operations 
	 */
	public String encodeDecode(String msg) {
		// validate that the message is not null or length == 0
		// if it is the case, just return the original message
		if(msg == null || msg.length() == 0)
			return msg;
		// if the key is "" we return the original message
		if(key.length() == 0)
			return msg;
		// make an array of CharAndBits from both the message and the key
		CharAndBits[] m = CharAndBits.newCharAndBitsArray(msg);
		CharAndBits[] k = CharAndBits.newCharAndBitsArray(key);
		// and call the method that performs the XOR operation
		String encodeDecodeValue = CharAndBits.xorArray(m, k);
		return encodeDecodeValue;
	}

	/**
	 * A quick and dirty method to return the key duplicated enough times
	 * so it will have the length of the message.
	 * This is just for printing purpose only both in the main() method and in the GUI.
	 * The method is not involved in the encoding/decoding process itself
	 */
	public String dupKey(int msgLen) {
		// if the key is invalid no 
		if(key.length() == 0)
			return "";
		String dup = key;
		while(dup.length() < msgLen)
			dup += key;
		return dup.substring(0, msgLen);
	}

	/**
	 * To test the class
	 */
	public static void main(String[] args) {

		//-------- unit tests to see that the whole thing works --------
		String msg = "DreamInCode";
		String key = "dave";
		// create the Xor object
		Xor xor = new Xor(key);
		
		// for print out purpose only get the key used (it will be as log as the message)
		String dupKey = xor.dupKey(msg.length());
		System.out.println("The original message is: \"" + msg + "\" the key used will be \"" + dupKey + "\"");
		// call the utility method for binary representation of the message
		String msgInBin = CharAndBits.toBinaryString(msg);
		System.out.println(msgInBin);
		
		// the repeated key in binary
		String keyInBin = CharAndBits.toBinaryString(dupKey);
		System.out.println(keyInBin);
		// build a series of -------
		char[] dash = new char[msgInBin.length()];
		for(int i = 0; i < msgInBin.length(); i++)
			dash[i] = '-';
		// print the serie of ------
		System.out.println(new String(dash));
		
		// encode the message which is the result of the XOR
		String encoded = xor.encodeDecode(msg);
		// display the encoded bits
		String encodedInBinary = CharAndBits.toBinaryString(encoded); 
		System.out.println(encodedInBinary);
		// display what is printable out of it
		System.out.println("The encrypted message is: \"" + CharAndBits.toAsciiString(encoded) + "\"");
		
		// now the reverse process
		System.out.println();
		System.out.println("The encoded message XORed with the key");
		System.out.println(encodedInBinary);			// encoded message
		System.out.println(keyInBin);					// key in binary
		System.out.println(new String(dash));           // the ------------
		
		// decode the encoded message calling the SAME method
		String decoded = xor.encodeDecode(encoded);
		// display the decoded message
		System.out.println(CharAndBits.toBinaryString(decoded));
		System.out.println("The decoded message is \"" + decoded + "\" is it the same as \"" + msg + "\": " + msg.equals(decoded));
		// ----------------------------- end of unit tests ---------------------------------
		
		// Now prompting the user
		Scanner scan = new Scanner(System.in);
		String userKey;
		// get a key of length > 0 from the user
		System.out.println();
		do {
			System.out.print("Enter the key to use: ");
			userKey = scan.nextLine();
		} while(userKey.length() == 0);
        
		// build the Xor object
		Xor userXor = new Xor(userKey);
		// get the message to encode/decode
		System.out.print("Enter message to encode: ");
		String userMsg = scan.nextLine();
		
		// generate the key that will be used for print out purpose only
		String userDupKey =  userXor.dupKey(userMsg.length());
		System.out.println("The original message is: \"" + userMsg + "\" the key used will be \"" + userDupKey + "\"");
		// call the utility method for binary representation of the message
		String userMsgInBin = CharAndBits.toBinaryString(userMsg);
		System.out.println(userMsgInBin);
		
		// the repeated key in binary
		String userKeyInBin = CharAndBits.toBinaryString(userDupKey);
		System.out.println(userKeyInBin);
		// build a series of -------
		char[] userDash = new char[userMsgInBin.length()];
		for(int i = 0; i < userMsgInBin.length(); i++)
			userDash[i] = '-';
		// print the serie of ------
		System.out.println(new String(userDash));
		
		// encode the message which is the result of the XOR
		String userEncoded = userXor.encodeDecode(userMsg);
		// display the encoded bits
		String userEncodedInBinary = CharAndBits.toBinaryString(userEncoded); 
		System.out.println(userEncodedInBinary);
		// display what is printable out of it
		System.out.println("The encrypted message is: \"" + CharAndBits.toAsciiString(userEncoded) + "\"");
		
		// now the reverse process
		System.out.println();
		System.out.println("The encoded message XORed with the key");
		System.out.println(userEncodedInBinary);			// encoded message
		System.out.println(userKeyInBin);					// key in binary
		System.out.println(new String(userDash));           // the ------------
		
		// decode the encoded message calling the SAME method
		String userDecoded = userXor.encodeDecode(userEncoded);
		// display the decoded message
		System.out.println(CharAndBits.toBinaryString(userDecoded));
		System.out.println("The decoded message is \"" + userDecoded + "\" is it the same as \"" + userMsg + "\": " + userMsg.equals(userDecoded));
		
		
	}
	
}




Is This A Good Question/Topic? 1
  • +

Page 1 of 1