4
\$\begingroup\$

I came along these two ciphers in my cryptography book and though I'd implement them just for fun. After that I went on developing a cipher based on these two that minimize the weakness of each cipher alone. I would appreciate your expert opinions on it and if it would be possible to crypt-analyse.

import java.util.*;
import java.io.*;

class CaesarCipher{
static String encrypt(String s, int key){
List<Character> arr = Arrays.asList('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
StringBuilder str = new StringBuilder();
int index;
for(int i = 0; i< s.length(); i++)
{
if(arr.contains(Character.toUpperCase(s.charAt(i))) == false)
    str.append(s.charAt(i));
else{
index = arr.indexOf(Character.toUpperCase(s.charAt(i)));
str.append(arr.get(Math.abs(index + key) % arr.size()));
   }
}

return str.toString();
}

}
class VigenereCaesar{

static String encrypt(String s, String k, int h){
List<Character> arr = Arrays.asList('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',' ','0','1','2','3','4','5','6','7','8','9');
Random rand = new Random(h);
StringBuilder str = new StringBuilder();
int index1, index2;
int hash = h;
String key = k;
for(int i = 0; i< s.length(); i++)
{
  if(i % k.length() == 0){
   key = CaesarCipher.encrypt(key,++hash);
   Collections.shuffle(arr,rand);
  }
 if(arr.contains(Character.toUpperCase(s.charAt(i))) == false)
  str.append(s.charAt(i));
 else{
 index1 = arr.indexOf(Character.toUpperCase(s.charAt(i)));
 index2 = arr.indexOf(Character.toUpperCase(key.charAt(i % key.length())));
 str.append(arr.get((index1 + index2) % arr.size()));
 }
 }

return str.toString();
}
static String encryptFile(BufferedReader buff, String k,int h) throws IOException{
List<Character> arr = Arrays.asList('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',' ','0','1','2','3','4','5','6','7','8','9');
Random rand = new Random(h);
StringBuilder str = new StringBuilder();
int index1, index2;
int hash = h;
String key = k;
String input = buff.readLine();
int count = 0;
while(input != null){
for(int i = 0; i< input.length(); i++)
{
  if(count % k.length() == 0){
  key = CaesarCipher.encrypt(key,++hash);
  Collections.shuffle(arr,rand);
}
  if(arr.contains(Character.toUpperCase(input.charAt(i))) == false)
  str.append(input.charAt(i));
 else{
    index1 = arr.indexOf(Character.toUpperCase(input.charAt(i)));
    index2 = arr.indexOf(Character.toUpperCase(key.charAt(i % key.length())));
    str.append(arr.get((index1 + index2) % arr.size()));
    }
    count++;
  }
  str.append("\n");
  input = buff.readLine();
 }
 return str.toString();
 }
 static String decrypt(String s, String k,int h){
 List<Character> arr = Arrays.asList('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',' ','0','1','2','3','4','5','6','7','8','9');
 Random rand = new Random(h);
 StringBuilder str = new StringBuilder();
 int index1, index2;
 int hash = h;
 String key = k;
 for(int i = 0; i < s.length(); i++)
 {

  if(i % key.length() == 0){
  key = CaesarCipher.encrypt(key,++hash);
  Collections.shuffle(arr,rand);
 }
 if(arr.contains(Character.toUpperCase(s.charAt(i))) == false)
  str.append(s.charAt(i));
else{
index1 = arr.indexOf(Character.toUpperCase(s.charAt(i)));
index2 = arr.indexOf(Character.toUpperCase(key.charAt(i % key.length())));
str.append(arr.get((index1 + (arr.size() - index2) )% arr.size()));
System.out.println(s.charAt(i) + " ---> " + arr.get((index1 + (arr.size() - index2) )% arr.size()));
}
}

 return str.toString();
}

static String decryptFile(BufferedReader buff, String k,int h) throws IOException{
List<Character> arr = Arrays.asList('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',' ','0','1','2','3','4','5','6','7','8','9');
Random rand = new Random(h);
StringBuilder str = new StringBuilder();
int index1, index2;
int hash = h;
String key = k;
String input = buff.readLine();
int count = 0;
while(input!= null){
for(int i = 0; i < input.length(); i++)
{
    if(count % key.length() == 0){
    key = CaesarCipher.encrypt(key,++hash);
    Collections.shuffle(arr,rand);
    }
    if(arr.contains(Character.toUpperCase(input.charAt(i))) == false)
    str.append(input.charAt(i));
    else{
    index1 = arr.indexOf(Character.toUpperCase(input.charAt(i)));
    index2 = arr.indexOf(Character.toUpperCase(key.charAt(i % key.length())));
    str.append(arr.get((index1 + (arr.size() - index2) )% arr.size()));
    }
        count++;
    }
    str.append("\n");
    input = buff.readLine();
   }
  return str.toString();
  }
}
 public class MyCipher{
  public static void main(String[] args) throws IOException{
  if(args.length == 2){
    try{
     BufferedReader fileBuff = new BufferedReader(new FileReader(args[0]));

  if(!args[1].equals("encrypt") && !args[1].equals("decrypt")){
    System.out.println("Error Usage:\n- Encryption: java MyCipher fileName encrypt\n- Decryption: java MyCipher fileName decrypt");
    System.exit(1);
   }
   if(args[1].equals("encrypt")){
    System.out.println("Enter encryption key (The longer and more random, the harder it gets to decrypt!) :");
    String key; int hash = 0;
    key = new BufferedReader(new InputStreamReader(System.in)).readLine();
    System.out.println("Enter hash:");
    try{
    hash = Integer.parseInt(new BufferedReader(new InputStreamReader(System.in)).readLine());
    }catch(NumberFormatException e)
    {
    System.out.println("Hash must be a whole number.");
    System.exit(1);
    }
    System.out.println("Encrypting file...");
    String out = VigenereCaesar.encryptFile(fileBuff,key,hash);
    try{
        PrintWriter writer = new PrintWriter(args[0] + " - Encrypted");
        writer.write(out);
        writer.close();
    }catch(IOException exc){
        System.out.println("Error: Could not write to file." + exc.getMessage());
        System.exit(1);
    }
    System.out.println("File encryption complete.");
    System.out.println("\n--------------------------------------------------------------------");
    System.out.println("< Created by Issa Khoury. I appreciate comments at: i_kh@icloud.com >\n");
    System.exit(0);
    }
    else if(args[1].equals("decrypt")){
    System.out.println("Enter decryption key:");
    String key;
    int hash = 0;
    key = new BufferedReader(new InputStreamReader(System.in)).readLine();
    System.out.println("Enter hash:");
    try{
    hash = Integer.parseInt(new BufferedReader(new InputStreamReader(System.in)).readLine());
    }catch(NumberFormatException e)
    {
    System.out.println("Hash must be a whole number.");
    System.exit(1);
    }
    System.out.println("Decrypting file...");
    String out = VigenereCaesar.decryptFile(fileBuff,key,hash);

    try{
        PrintWriter writer = new PrintWriter(args[0] + "-Decrypted");
        writer.write(out);
        writer.close();
    }catch(IOException exc){
        System.out.println("Error: Could not write to file." + exc.getMessage());
        System.exit(1);
    }
    System.out.println("File decryption complete.");
    System.out.println("\n--------------------------------------------------------------------");
    System.out.println("< Created by Issa Khoury. I appreciate comments at: i_kh@icloud.com >\n");
    System.exit(0);
    }   
}catch(IOException exc){
        System.out.println("Error: Cannot procces file. " + exc.getMessage());
        System.exit(1);
  }
 }
  if(args.length != 1){
  System.out.println("Error Usage:\n- Encryption: java MyCipher encrypt\n- Decryption: java MyCipher decrypt");
  System.exit(1);
}
  if(args[0].equals("encrypt")){
  System.out.println("Enter encryption key (The longer and more random, the harder it gets to decrypt!) :");
  String key; int hash = 0;
  key = new BufferedReader(new InputStreamReader(System.in)).readLine();
  System.out.println("Enter hash:");
  try{
  hash = Integer.parseInt(new BufferedReader(new InputStreamReader(System.in)).readLine());
}catch(NumberFormatException e)
{
  System.out.println("Hash must be a whole number.");
  System.exit(1);
}
  System.out.print("Plaintext: ");
  String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
  String out = VigenereCaesar.encrypt(s,key,hash);
  System.out.println("-----------\nCiphertext: " + out);
  System.out.println("\n--------------------------------------------------------------------");
  System.out.println("< Created by Issa Khoury. I appreciate comments at: i_kh@icloud.com >\n");
  System.exit(0);
}
  else if(args[0].equals("decrypt")){
  System.out.println("Enter decryption key:");
  String key;
  int hash = 0;
  key = new BufferedReader(new InputStreamReader(System.in)).readLine();
  System.out.println("Enter hash:");
  try{
  hash = Integer.parseInt(new BufferedReader(new InputStreamReader(System.in)).readLine());
}catch(NumberFormatException e)
{
  System.out.println("Hash must be a whole number.");
  System.exit(1);
}
  System.out.print("Ciphertext: ");
  String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
  String out = VigenereCaesar.decrypt(s,key,hash);
  System.out.println("\n-----------\nPlaintext: " + out);
  System.out.println("\n--------------------------------------------------------------------");
  System.out.println("< Created by Issa Khoury. I appreciate comments at: i_kh@icloud.com >\n");
  System.exit(0);
}
  else{
  System.out.println("Error Usage:\n- Encryption: java MyCipher encrypt\n- Decryption: java MyCipher decrypt");
  System.exit(1);
   }
  }
 }
\$\endgroup\$
  • \$\begingroup\$ Hello there and Welcome to Code Review. I added the relevant language tag and tags for the ciphers you mention. Note that the cryptography-tag is explicitly for higher-end encryption algorithms. Caesar and Vigenere are just too simple to crack to count into that tag, so I removed it :) \$\endgroup\$ – Vogel612 Jul 6 '16 at 9:22
  • 1
    \$\begingroup\$ Regarding cryptanalysis: both the Caesar cipher and Vigenère cipher are considered trivially crackable based on letter frequency analysis. There really isn't any way to make these ciphers secure. \$\endgroup\$ – 200_success Jul 6 '16 at 10:07
  • \$\begingroup\$ Yes I know their weakness, but I'm checking if the way I combined them together mask their weakness. \$\endgroup\$ – Issa Khoury Jul 6 '16 at 10:09
  • \$\begingroup\$ @200_success Vigenerè with a (truly) random key as long as the text cannot be cracked right? \$\endgroup\$ – Caridorc Jul 7 '16 at 14:01
  • 2
    \$\begingroup\$ Yes, if I am not mistaken also known as One Time Pad. \$\endgroup\$ – Issa Khoury Jul 7 '16 at 14:03
2
\$\begingroup\$

Indentation and braces style

The indentation is frankly messy, and I don't think it's entirely due to the Markdown formatting.

Also, please be consistent in your braces style. For example:

if(arr.contains(Character.toUpperCase(s.charAt(i))) == false)
    str.append(s.charAt(i));
else {
    index = arr.indexOf(Character.toUpperCase(s.charAt(i)));
    str.append(arr.get(Math.abs(index + key) % arr.size()));
}

There's no need to skim on the extra { and } characters, and without them it makes scanning the code slightly harder, IMHO.

Negating if conditions

if(arr.contains(Character.toUpperCase(s.charAt(i))) == false) { /* ... */ }

That can be better expressed in the following way without using == false:

if(!arr.contains(Character.toUpperCase(s.charAt(i)))) { /* ... */ }

Variables names

In VigenereCaesar:

static String encrypt(String s, String k, int h){ /* ... */ }

It's not immediately obvious what these variables represent. Using the full form is recommended.

Command-line parsing

You may want to consider using some third-party libraries to help you parse command-line options, instead of doing yourself.

try-with-resource

You should also be using try-with-resource once for safe and efficient handling of your Scanner instance and the underlying System.in input stream. For example:

public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
        String mode = getMode(scanner);
        if (mode.equals("encrypt")) {
            String key = getKey(scanner);
            String hash = getHash(scanner);
            // do something with key and hash
        } else if (mode.equals("decrypt")) {
            // similar to above
        } else {
            // Indicate input error
        }
    }
}

In the methods getMode(Scanner), getKey(Scanner) and getHash(Scanner), they can simply call the appropriate methods to get user input, and throws IOException to propagate any I/O exceptions back to the main() method. Validation errors should be handled within the methods though, so that they can be expected to return one result.

Constant List collections

You are declaring Arrays.asList('A', 'B', 'C', /* ... */ 'Z') repetitively. You can consider putting it as an unmodifiable public static final List<Character> collection, and then only for the case where you want to shuffle it, create a new instance based on it to do so.

\$\endgroup\$
  • \$\begingroup\$ Thank you very much, so you are right about formatting errors, I am new to pasting code here and it was tedious to indent all the lines 4 spaces to make them a code snippet. And regarding the rest, yes you are right I am not following best practices because I was coding in ad hoc manner just to see the code work correctly. And about the scanner object I never used it before, I will look into it. I appreciate the time you've took to try and "parse" my code. May I have feedback if it compiled and worked correctly ? \$\endgroup\$ – Issa Khoury Jul 12 '16 at 8:02
  • \$\begingroup\$ @IssaKhoury we kind of expect code to be compilable and working correctly in the first place... does it for you? :) \$\endgroup\$ – h.j.k. Jul 12 '16 at 8:22
  • \$\begingroup\$ Haha yes, refactoring always comes as a second stage. I tend to crush bugs first and wait till most of the features that come to my mind are implemented before looking at code design, since adding features in a small application will surely mess up the code, so do it once at a final stage. \$\endgroup\$ – Issa Khoury Jul 12 '16 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.