import java.util.ArrayList; import java.util.Arrays; /** * Data Encryption Standard with Cipher Text Stealing * CPSC 428 project * @author Joshua Wilkins * @version 1.0 2008 */ public class DESCTS { private static ArrayList<byte[]> preCryptedBlocks; //inputs: organized in blocks of size 8. private static ArrayList<byte[]> resultingBlocks; //outputs: organized in blocks of size 8. //must be same size as the inputs private static byte[] storage; //temporary byte array used in the encrypt & decrypt functions private static int size; //variable used to store the size/length. /** * Empty Default Constructor */ public DESCTS() { } /** * Decrypts the given byte array using the Cipher Text Stealing mode * @param ctBytes ciphertext to be decrypted * @param key key needed for decryption * @param IV initial vector * @return byte[] the same size as ctBytes * @throws java.lang.Exception */ public static byte[] decrypt(byte[] ctBytes, javax.crypto.SecretKey key, byte[] IV) throws java.lang.Exception{ byte[] decrypted = null; if(ctBytes.length < 8) { throw new Exception(); } if(ctBytes.length == 8) { byte[] tempByteArray = DES.decrypt(ctBytes, key); decrypted = exclusiveOr(tempByteArray, IV); } if(ctBytes.length > 8) { preCryptedBlocks = splitByteArray(ctBytes); boolean isLastArraySizeEight = checkLastArray(preCryptedBlocks); //System.out.println(isLastArraySizeEight); if(isLastArraySizeEight) { resultingBlocks = easyDecryptCase(preCryptedBlocks, IV, key); decrypted = buildByteArray(resultingBlocks); } else { resultingBlocks = hardDecryptCase(preCryptedBlocks, IV, key); decrypted = buildByteArray(resultingBlocks); } } return decrypted; } /** * Encrypts the given byte array using Cipher Text Stealing * @param ptBytes plaintext to be encrypted * @param key given key needed for encryption * @param IV initial vector * @return byte[] the same size as ptBytes * @throws java.lang.Exception */ public static byte[] encrypt(byte[] ptBytes, javax.crypto.SecretKey key, byte[] IV) throws java.lang.Exception{ byte[] encrypted = null; size = ptBytes.length; if(ptBytes.length < 8) { throw new Exception(); } if(ptBytes.length == 8) { byte[] tempByteArray = exclusiveOr(ptBytes, IV); encrypted = DES.encrypt(tempByteArray, key); } if(ptBytes.length > 8) { preCryptedBlocks = splitByteArray(ptBytes); boolean isLastArraySizeEight = checkLastArray(preCryptedBlocks); if(isLastArraySizeEight) { resultingBlocks = easyEncryptCase(preCryptedBlocks, IV, key); encrypted = buildByteArray(resultingBlocks); } else { resultingBlocks = hardEncryptCase(preCryptedBlocks, IV, key); encrypted = buildByteArray(resultingBlocks); } } return encrypted; } /** * Handles the decryption case with padding * @param arraylist * @param IV * @param key * @return * @throws Exception */ private static ArrayList<byte[]> hardDecryptCase(ArrayList<byte[]> arraylist, byte[] IV, javax.crypto.SecretKey key) throws Exception { ArrayList<byte[]> result = new ArrayList<byte[]>(); for(int i = 0; i < arraylist.size(); i++) { if(i == 0){ //base result.add(exclusiveOr((DES.decrypt(arraylist.get(i), key)),IV)); } else { if(i == arraylist.size() - 1) { storage = DES.decrypt(arraylist.get(i-1), key); byte[] temp = arraylist.get(i); byte[] temp8 = new byte[8]; for (int j = 0; j < temp.length; j++) { temp8[j] = temp[j]; } for (int k = temp.length -1; k < temp8.length; k++) { temp8[k] = storage[k]; } result.add(exclusiveOr((DES.decrypt(temp8, key)), arraylist.get(i-2))); byte[] tempA = new byte[temp.length]; for (int l = 0; l < temp.length; l++) { tempA[l] = (byte) (temp[l] ^ storage[l]); } result.add(tempA); } else { result.add(exclusiveOr((DES.decrypt(arraylist.get(i), key)), arraylist.get(i-1))); } } } return result; } /** * Handles the decryption case without padding * @param arraylist * @param IV * @param key * @return * @throws Exception */ private static ArrayList<byte[]> easyDecryptCase(ArrayList<byte[]> arraylist, byte[] IV, javax.crypto.SecretKey key) throws Exception { ArrayList<byte[]> result = new ArrayList<byte[]>(); for(int i = 0; i < arraylist.size(); i++) { if(i == 0) { //base result.add(exclusiveOr((DES.decrypt(arraylist.get(i), key)),IV)); } else { result.add(exclusiveOr((DES.decrypt(arraylist.get(i), key)), arraylist.get(i-1))); } } return result; } /** * Handles the encryption case with padding * @param arraylist * @param IV * @param key * @return ArrayList<byte[]> * @throws Exception */ private static ArrayList<byte[]> hardEncryptCase(ArrayList<byte[]> arraylist, byte[] IV, javax.crypto.SecretKey key) throws Exception { ArrayList<byte[]> result = new ArrayList<byte[]>(); for(int i = 0; i < arraylist.size(); i++) { if(i == 0) { //base case, first block of 8 bytes result.add(DES.encrypt(exclusiveOr(arraylist.get(i),IV),key)); } else { if(i == arraylist.size() - 2) { //next to last case, save the output to storage dont add to final storage = DES.encrypt(exclusiveOr(arraylist.get(i),result.get(i-1)), key); } else if(i == arraylist.size() - 1) { byte[] temp = arraylist.get(i); byte[] temp8 = new byte[8]; for(int j = 0; j < temp.length; j++){ temp8[j] = temp[j]; //builds the first part of the 8 byte array with whats available. } for(int k = temp.length; k < temp8.length; k++) { temp8[k] = 0x00; //padding } result.add(DES.encrypt(exclusiveOr(storage, temp8),key)); byte[] tempFinal = new byte[temp.length]; for (int l = 0; l < tempFinal.length; l++) { tempFinal[l] = storage[l]; } result.add(tempFinal); } else { result.add(DES.encrypt(exclusiveOr(arraylist.get(i),result.get(i-1)), key)); } } } return result; } /** * Handles the encryption case without padding * @param arraylist * @param IV * @param key * @return ArrayList<byte[]> * @throws Exception */ private static ArrayList<byte[]> easyEncryptCase(ArrayList<byte[]> arraylist, byte[] IV, javax.crypto.SecretKey key) throws Exception { ArrayList<byte[]> result = new ArrayList<byte[]>(); for(int i = 0; i < arraylist.size(); i++) { if(i == 0) { //base case: blocks of 8 bytes result.add(DES.encrypt(exclusiveOr(arraylist.get(i),IV),key)); } else { //adds the other blocks of 8 bytes to the final result. result.add(DES.encrypt(exclusiveOr(arraylist.get(i),result.get(i-1)), key)); } } return result; } /** * Checks to see if the last byte array's size is equal to 8 * @param arraylist * @return boolean */ private static boolean checkLastArray(ArrayList<byte[]> arraylist) { boolean checked = false; if(arraylist.get(arraylist.size()-1).length == 8) { checked = true; } return checked; } /** * returns a single byte array from the blocks * @param arraylist * @return byte[] */ private static byte[] buildByteArray(ArrayList<byte[]> arraylist) { byte[] temp = new byte[size]; int counter = 0; for(int i = 0; i < arraylist.size(); i++) { for(int j=0; j < arraylist.get(i).length; j++) { if(counter < size) { temp[counter] = arraylist.get(i)[j]; counter++; } } } return temp; } /** * Returns the results of an exclusive Or Operation of two size 8 byte arrays * @param byteArrayA * @param byteArrayB * @return byte[] of size 8 */ private static byte[] exclusiveOr(byte[] byteArrayA, byte[] byteArrayB) { byte[] temp = new byte[8]; for (int i = 0; i < 8; i++) { temp[i] = (byte) (byteArrayA[i] ^ byteArrayB[i]); } return temp; } /** * Splits the byte array into 8 byte blocks * @return ArrayList<byte[]> */ private static ArrayList<byte[]> splitByteArray(byte[] byteArray) { int counterA = 0; ArrayList<byte[]> arraylist = new ArrayList<byte[]>(); while(counterA < byteArray.length) { byte[] tempByteArray = new byte[8]; if ((byteArray.length - counterA) < 8) { tempByteArray = new byte[byteArray.length - counterA]; for(int i = 0; i < tempByteArray.length; i++) { tempByteArray[i] = byteArray[counterA]; counterA++; } } else { for(int i = 0; i < 8; i++) { tempByteArray[i] = byteArray[counterA]; counterA++; } } arraylist.add(tempByteArray); } return arraylist; } }