Instructions
Requirements and Specifications
Source Code
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Scanner;
public class AES {
private static PrintWriter output;
public static void print() {
print("");
}
public static void print(String s) {
System.out.println(s);
output.println(s);
}
public static void main(String[] args) {
String input = null;
String key = null;
try(Scanner scanner = new Scanner(System.in)) {
System.out.print("Enter the name of the input plaintext file: ");
String inputFile = scanner.nextLine();
input = new String(Files.readAllBytes(Paths.get(inputFile)));
System.out.print("Enter the name of the input key file: ");
String keyFile = scanner.nextLine();
key = new String(Files.readAllBytes(Paths.get(keyFile)));
System.out.print("Enter the name of the output ciphertext file: ");
String outputFile = scanner.nextLine();
output = new PrintWriter(outputFile);
}
catch (IOException e) {
System.out.println("Can not read input file");
return;
}
input = preprocessing(input);
input = substitution(input, key);
input = padding(input);
input = shiftRows(input);
char[] enc = parityBit(input);
mixColumns(enc);
output.close();
}
private static String preprocessing(String input) {
print("Preprocessing:");
StringBuilder builder = new StringBuilder();
for(char c : input.toCharArray()) {
if (c >= 'A' && c <= 'Z') {
builder.append(c);
}
}
print(builder.toString());
print();
return builder.toString();
}
private static String substitution(String input, String key) {
print("Substitution:");
StringBuilder builder = new StringBuilder();
for(int i = 0; i<input.length(); i++) {
char keyChar = key.charAt(i % key.length());
char c = input.charAt(i);
c = (char)((c - 'A' + keyChar - 'A') % 26 + 'A');
builder.append(c);
}
print(builder.toString());
print();
return builder.toString();
}
private static String padding(String input) {
print("Padding:");
int len = input.length();
int blocks = (len + 15) / 16;
int paddingLength = blocks * 16 - len;
StringBuilder builder = new StringBuilder(input);
for(int i = 0; i<paddingLength; i++) {
builder.append("A");
}
String result = builder.toString();
for(int i = 0; i<blocks; i++) {
for (int j = 0; j<4; j++) {
print(result.substring(16*i + 4*j, 16*i+4*(j+1)));
}
print();
}
print();
return result;
}
private static String shiftRows(String input) {
print("ShiftRows:");
int len = input.length();
int blocks = len / 16;
StringBuilder builder = new StringBuilder();
for(int i = 0; i<blocks; i++) {
for (int j = 0; j<4; j++) {
String row = input.substring(16*i + 4*j, 16*i+4*(j+1));
builder.append(row, j, 4);
builder.append(row, 0, j);
}
}
String result = builder.toString();
for(int i = 0; i<blocks; i++) {
for (int j = 0; j<4; j++) {
print(result.substring(16*i + 4*j, 16*i+4*(j+1)));
}
print();
}
print();
return result;
}
private static int[] getBits(char c) {
int[] bits = new int[8];
int a = c;
for (int i = 0; i<8; i++) {
bits[i] = a % 2;
a /= 2;
}
return bits;
}
private static char fromBits(int[] bits) {
int a = 0;
int cf = 1;
for (int i = 0; i<8; i++) {
a += cf * bits[i];
cf *= 2;
}
return (char) a;
}
private static char parityBitChar(char c) {
int ones = 0;
for (int b : getBits(c)) {
if (b == 1) {
ones++;
}
}
if (ones % 2 == 1) {
return (char)(c + 128);
}
else {
return c;
}
}
private static void sum(int[] a, int[] b) {
for(int i = 0; i<8; i++) {
a[i] = (a[i] + b[i])%2;
}
}
private static int[] rgfMul(char c, int k) {
int[] bits = getBits(c);
if (k != 2 && k != 3) {
return bits;
}
boolean carry = bits[7] == 1;
int[] shifted = getBits(c);
for (int i = 7; i>0; i--) {
shifted[i] = shifted[i-1];
}
shifted[0] = 0;
if (k == 2) {
if (carry) {
sum(shifted, new int[]{1,1,0,1,1,0,0,0});
}
return shifted;
}
sum(bits, shifted);
if (carry) {
sum(bits, new int[]{1,1,0,1,1,0,0,0});
}
return bits;
}
private static char[] mutliply123(char[] col) {
int[] bits = rgfMul(col[0], 2);
sum(bits, rgfMul(col[1], 3));
sum(bits, rgfMul(col[2], 1));
sum(bits, rgfMul(col[3], 1));
char c0 = fromBits(bits);
bits = rgfMul(col[0], 1);
sum(bits, rgfMul(col[1], 2));
sum(bits, rgfMul(col[2], 3));
sum(bits, rgfMul(col[3], 1));
char c1 = fromBits(bits);
bits = rgfMul(col[0], 1);
sum(bits, rgfMul(col[1], 1));
sum(bits, rgfMul(col[2], 2));
sum(bits, rgfMul(col[3], 3));
char c2 = fromBits(bits);
bits = rgfMul(col[0], 3);
sum(bits, rgfMul(col[1], 1));
sum(bits, rgfMul(col[2], 1));
sum(bits, rgfMul(col[3], 2));
char c3 = fromBits(bits);
return new char[]{c0, c1, c2, c3};
}
private static char[] parityBit(String input) {
print("ParityBit:");
char[] result = new char[input.length()];
for(int i = 0; i<input.length(); i++) {
result[i] = parityBitChar(input.charAt(i));
}
StringBuilder toPrint = new StringBuilder();
for(int i = 0; i<result.length/4; i++) {
for(int j = 0; j<4; j++) {
toPrint.append(String.format("%x", (int)result[4*i+j])).append(" ");
}
toPrint.append(System.lineSeparator());
}
print(toPrint.toString());
print();
return result;
}
private static void mixColumns(char[] chars) {
print("MixColumns:");
int blocks = chars.length / 16;
for (int i = 0; i<blocks; i++) {
for (int j = 0; j<4; j++) {
char[] col = new char[]{chars[16*i + j],chars[16*i + j + 4],chars[16*i + j + 8],chars[16*i + j + 12] };
char[] mixed = mutliply123(col);
chars[16*i + j] = mixed[0];
chars[16*i + j + 4] = mixed[1];
chars[16*i + j + 8] = mixed[2];
chars[16*i + j + 12] = mixed[3];
}
}
StringBuilder toPrint = new StringBuilder();
for(int i = 0; i<chars.length/4; i++) {
for(int j = 0; j<4; j++) {
toPrint.append(String.format("%x", (int)chars[4*i+j])).append(" ");
}
toPrint.append(System.lineSeparator());
}
print(toPrint.toString());
print();
}
}