Page 1 of 1

Secret Code VI - The XOR operator Second step in the computer era Rate Topic: -----

#1 pbl  Icon User is offline

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

Reputation: 8342
  • View blog
  • Posts: 31,880
  • Joined: 06-March 08

Posted 26 September 2010 - 04:28 PM

Before going into the RSA stuff one thing to really understand is the use of the XOR operator.

Following the tutorial about Swap and Rotate here is another one using the XOR operator.

As usual a console application named Xor.java is provided.
As is the previous tutorial a class CharAndBits.jave is provided for printing (both at the console and in the GUI) the bits representation of every byte.

This version of CharAndBits has some improvments compare the previous version coming from Secret Code V. You can take it and overload the previous copy you had if you cut & pasted the code from the previous tutorial, our code from SwapAndRotate will still works.

As usual we present a console version:
- Xor.java which has in its main() method unit test
- XorGui.java that uses the preceeding one and dynamically show the encryption/decryption processes

CharAndBits.java

/**
 * 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 * 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();
	}
}



The console version of the encoding/decoding algorithm using XOR


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));
		
		
	}
	
}




And now the GUI XorGui.java
This GUI is also a good example on how to use JTable with variable number of columns


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
 * In this GUI we cannot prompt for a String to decode as XOring 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 after the XOR operation
 * This GUI is greatly inspired from the SwapAndRotateGui of Secret Code V tutorial
 * 
 * Note that if a letter of the message and the corresponding letter of the key are equal
 * the encrypted letter is 00000000 
 * If message is ABC and key 123 the encrypted versions should be the same :-)
 */
public class XorGui extends JFrame {

	private static final long serialVersionUID = 1L;

	// the Xor class to encode/decode
	private Xor xor;

	// the key to use
	private JTextField keyText;
	// the letter of the key for every letter in the key
	private char[] longKey;
	// The message to encode
	private JTextField clearTextIn;

	// the original message
	private char[] msgChar = new char[0];
	// the encoded messages
	private char[] msgEncoded;
	// 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 first column of the jTable
	private static final String[] firstCol = {"Message", "Msg Bin", "Key Bin", "XOR Encoded", "Encoded Ascii", "Key Bin", "XOR Decoded", "Decoded Ascii"};
	// mnemonic for more descriptive values in the AbstractModel
	private static final int ORIG = 0, ORIG_BIN = 1, KEY1_BIN = 2, CRYPTED_BIN = 3, CRYPTED = 4, KEY2_BIN = 5, DECRYPTED_BIN = 6, DECRYPTED = 7;

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

		// the Xor object init the key to ""
		xor = new Xor("");
		longKey = xor.dupKey(0).toCharArray();
		// the Listener the key changes
		DocumentListener dc = new KeyListener();
		// The NORTH region will contain a JPanel where the key and the message can be entererd
		JPanel north = new JPanel(new GridLayout(5, 1, 2, 2));
		// the key
		north.add(createCenteredLabel("The Key"));
		keyText = new JTextField(50);
		keyText.getDocument().addDocumentListener(dc);
		north.add(keyText);
		// the message
		north.add(createCenteredLabel("Enter the message to encode 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();

		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, 700, 320);
		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;
	}

	/**
	 * The key has changed 
	 */
	private void updateKeyString() {
		// update key in the Xor object from the key JTextField
		xor.setKey(keyText.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
		longKey = xor.dupKey(line.length()).toCharArray();
		// build the encrypted message
		String encoded = xor.encodeDecode(line);
		// in a char[] for display purpose
		msgEncoded = encoded.toCharArray();
		// build the decrypted char[]
		msgDecoded = xor.encodeDecode(encoded).toCharArray();
		// 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 + 1;
		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 XorGui();
	}

	/**
	 * 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 message + the first column
		public int getColumnCount() {
			return  msgChar.length + 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
			--column;
			// verify that we have data for this column
			if(column >= msgChar.length || column >= longKey.length)
				return "";

			// OK generate its title which is the letter of the key used
			return "#" + (column+1) + " Key: \"" + longKey[column] + "\"";
		}

		// 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];
			--col;
			// validate first the col it should be contained in the mesage
			if(col >= msgChar.length)
				return "";
			
			// the CharAndBits to display init to null we will check it later
			CharAndBits cab = null;

			// depending of the column display the correct text field
			switch(row) {
			    // the original message we juts return the character of that row
				case ORIG:
					return "      >" + msgChar[col] + "<";
				// the binary stransaltion of that letter
				case ORIG_BIN:
					cab = new CharAndBits(msgChar[col]);
					break;
	            // the binary representation of the key (same one at both row)
				case KEY1_BIN:
				case KEY2_BIN:
					if(col >= longKey.length)
						return "";
					cab = new CharAndBits(longKey[col]);
					break;		
				// the binary representation of the encrypted letter of the message
				case CRYPTED_BIN:
					cab = new CharAndBits(msgEncoded[col]);
					break;	
				// the ASCII representation (if it exists) of this letter of the message
				case CRYPTED:
					CharAndBits c = new CharAndBits(msgEncoded[col]);
					if(c.isPrintable())
						return "      >" + c.getPrintableChar() + "<";
					else
						return "      -";
				// the decrypted message back should be the same of ORIG_BIN
				case DECRYPTED_BIN:
					if(col >= msgDecoded.length)
						break;
					cab = new CharAndBits(msgDecoded[col]);
					break;
				// the letter of the message back into Ascii
				case DECRYPTED:
					return "      >" + msgDecoded[col] + "<";					
			}
			// 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();
		}

	}

}



Have fun

Is This A Good Question/Topic? 0
  • +

Replies To: Secret Code VI - The XOR operator

#2 nasrul  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 08-February 11

Posted 13 March 2011 - 09:35 AM

Hello sir,im new to this type of encryption.Is it this XOR function using LFSR?if it's use LFSR,can you show where is the function that do the LFSR.really2 eager to know.Thanks

Edited by macosxnerd101: Please do NOT quote the person above you, especially when their post is extremely long.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1