Search code examples
javaarraysencryptioncaesar-ciphervigenere

Vignere Cipher encryption/decryption using arrays


I need to first encrypt and then decrypt a message using the Vigenere cypher. This is how it should work

example message:                                 "c  a  t  s  _  d  o  g  s"
keyword "rats":                                   r  a  t  s  r  a  t  s  r
order of the letter in the message (start at a=0):2  0  19 18 () 3  14 6  18
order of the letter in the keyword:               17 0  19 18 17 0  19 18 17
sum of the two orders:                            19 0  38 36 17 3  39 24 35
new letter for the message*                       t  a  m  k     d  h  y  j
encrypted message = "tamk uoyk"

Note: if the sum > 26 then we subtract 26 from the sum to get a cyclical alphabet. Example:

z + b = 25 + 1 = 26; 26 - 26 = 0 --> a

I have written the methods to obtain the numerical value of the keyword, and also two methods that "add" or "subtract" individual letters and two methods that perform the caesar encoding/decoding (simply shifting the entire message by an int to the right in the alphabet, or to the left to decrypt).

The piece that I really need help for is to how to create a for loop that's going to repeat the keyword the appropriate amount of times (to have the same length as the message) and proceed to the obtainKeys method to get the numerical values of the repeated key.

Here is my entire program; the part I am struggling with is at the end (Q2f)

import java.util.Arrays;
public class Cypher {
    public static void main(String[] args) {
        System.out.println(charRightShift('z', 3));
        System.out.println(charLeftShift('z', 3));

        String test = caesarEncode("cats and dogs", 5);
        System.out.println(test);
        System.out.println(caesarDecode(test, 5));
        obtainKeys("abcxyz");
        System.out.println(vigenereEncode("elephants", "rats"));
    }

    //Q2a-b
    //Generalized method for char shifts
    public static char charShift(char c, int n) {
        //value of n should be between 0 and 25
        if (Math.abs(n) < 0 || 25 < Math.abs(n)) {
            //returning the ascii value of '0' which
            //is nul & adding error message
            int zero = 0;
            c = (char) zero;
            throw new IllegalArgumentException("n has to be 0<=|n|<=25");
        }
        //character c should be a lower case latin letter
        //if not, we simply return c, the original character,
        //skipping this else if
        else if (c >= 'a' && c <= 'z') {
            c = (char) (c + n);
            if (c > 'z') {
                c = (char) (c - 26);
            } else if (c < 'a') {
                c = (char) (c + 26);
            }
        }
        return c;
    }

    //method that shifts the value of the character to the right
    public static char charRightShift(char c, int n) {
        c = charShift(c, n);
        return c;
    }

    //method that shifts the value of the character to the left
    public static char charLeftShift(char c, int n) {
        n = -n;
        c = charShift(c, n);
        return c;
    }

    //Q2c
    //method that shifts the message to the right by int 'key' characters
    public static String caesarEncode(String message, int key) {
        //transform string into char array
        char[] messageEncrypt = message.toCharArray();
        //for each char, we shift it by 'key' ints,
        //using charRightShift method
        for (int i = 0; i < messageEncrypt.length; i++) {
            char c = messageEncrypt[i];
            c = charRightShift(c, key);
            messageEncrypt[i] = c;
        }
        return new String(messageEncrypt);
    }

    //Q2d
    //method that shifts the message to the left by int 'key' characters
    public static String caesarDecode(String message, int key) {
        //transform string into char array
        char[] messageDecrypt = message.toCharArray();
        //for each char, we shift it by 'key' ints using charLeftShift
        for (int i = 0; i < messageDecrypt.length; i++) {
            char c = messageDecrypt[i];
            c = charLeftShift(c, key);
            messageDecrypt[i] = c;
        }
        return new String(messageDecrypt);
    }

    //Q2e
    //method to obtain the int array storing the numerical value of the String
    public static int[] obtainKeys(String s) {
        //creating int array where we're going to
        //store the numerical value of the String
        int[] keys = new int[s.length()];
        int j;
        //for each ascii value of the char in string s, we substract 97 to
        //get the lower case english alphabet character corresponding to it
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            j = c - 97;
            //now store every int in the int array
            keys[i] = j;
        }
        String keysString = Arrays.toString(keys);
        return keys;
    }

    //Q2f
    public static String vigenereEncode(String message, String keyword) {
        //for loop check if there are any 'illegal' characters in the keyword
        char[] kword = keyword.toCharArray();
        for (int i = 0; i < kword.length; i++) {
            char c = kword[i];
            if (c < 'a' || c > 'z') {
                throw new IllegalArgumentException(
                        "The keyword must only contain characters " +
                                "from the lower case English alphabet.");
            }
        }
        int[] numMessage = obtainKeys(message);
        int[] numKeyword = obtainKeys(keyword);
        for (int i = 0; i < message.length(); i++) {
            for (int j = 0; j < keyword.length(); i++) {
                //NOT SURE IF I NEED A NESTED LOOP HERE
                //WHAT TO DO HERE?
            }
        }
        return messageVigenere;
    }
}

Solution

  • You can do this by using the mod operation %.

    char[] messageArray = message.toCharArray();
    char[] encryptedMessage = new char[messageArray.length];
    int[] numKeyword = obtainKeys(keyword);
    int keywordLength = numKeyword.length;
    
    for(int i=0; i<message.length(); i++){
        int shiftAmount = numKeyword[i % keywordLength];
        char c = messageArray[i];
        c = charRightShift(c,shiftAmount);
        encryptedMessage[i] = c;
    }