Search code examples
javaencryptionvigenere

Confused about Vigenère cipher implementation in Java


Please can someone explain the line of code highlighted below. I don't understand at all how this line works.

You can use this example to help me:

input:   ATTACK  
keyword: LEMON  
res:     LXFOPV

I don't understand how that line helps encode A to L and other letter... ACSII involvement?

static String encrypt(String text, final String key) {
    String res = "";
    text = text.toUpperCase();
    for (int i = 0, j = 0; i < text.length(); i++) {
        char c = text.charAt(i);
        if (c < 'A' || c > 'Z') continue;
 ////////////////////////////////////////////////////////////////////////////
//please someone explain this line
        res += (char)((c + key.charAt(j) - 2 * 'A') % 26 + 'A'); 
////////////////////////////////////////////////////////////////////////

        j = ++j % key.length();
    }
    return res;
}

Solution

  • Background

    The code uses the ASCII value of the letters. The letters A-Z are ASCII values 65-90.

    The idea is to add the two letters together, but wrap around if the values goes above 90 (known as modular arithmetic). So 91 should actually be 65 (i.e. Z + 1 = A).

    Java provides a % operator for doing modular arithmetic (x % n). However, this is designed to work on a range of numbers 0→n-1. So, if we subtract 65 from each of our letters, we are then working in the range 0→25. This allows us to use the modulus operator (x % 26).

    This is what the code is doing:

    c + key.charAt(j) - 2 * 'A'
    

    This part adds the two letters together, but also subtracts 65 from each of them. It may be easier to understand if written as:

    (c - 'A') + (key.charAt(j) - 'A')
    

    You'll notice that you can do - 'A' as a convenient way of doing - 65.

    Now we have a value that's zero-based, but possibly larger than 25. So we then modulo it:

    (c + key.charAt(j) - 2 * 'A') % 26
    

    We then need to add 65 back to the value, to bring it back into the A-Z range for ASCII:

    (c + key.charAt(j) - 2 * 'A') % 26 + 'A'
    

    The only remaining step is to cast this to a char, since the result is an int by default:

    res += (char)((c + key.charAt(j) - 2 * 'A') % 26 + 'A'); 
    

    Example

    If the input is ATTACK and the keyword is LEMON, then at some point we are going to have to consider the input letter T (ASCII 84) and the key letter M (ASCII 77).

    Subtracting 65 from each, we have T=19 and M=12. Added together, we get 31.

    31 % 26 = 5. So we then calculate 5+65=70, which is the ASCII value for F.