4
\$\begingroup\$

EDIT:
I've created a repo and uploaded it to Maven (still in dev though). Any suggestion or pull request is more than appreciated. :)

https://github.com/enrichman/vigenere


I had the need to write a simple encryption algorithm (no security needs, it just needs to look a bit random) and I thought to add a bit of random things to a Vigenere cipher.

The idea was simply to create a random key of a fixed length, use it as key in the Vigenere, then append it to the encrypted text. Then encrypt again the whole text with the secret key.

In this way the same text, encrypted with the same key looks more random.

Any suggestion about the algorithm or implementation?

A simple run:

1)
Plaintext: HELLO
Encrypted: FF22LDAQ
Decrypted: HELLO

2)
Plaintext: HELLO
Encrypted: 7MYUS92X
Decrypted: HELLO

Maybe the last thing to add is a way to add the initial size to the encrypted text, but I don't want it to be fixed in some way.

public class Crypto {

    private static Random rand = new Random();
    private static char[] alphabet = new char[] {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            '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'
    };
    private static String KEY = "DSNJKDNFJKSNDJKNOCNSJCKNK";

    public static void main(String[] args) {

        int size = 3;
        String plaintext = "HELLO";

        System.out.println("Plaintext: " + plaintext);

        String enc = encrypt(plaintext, size);
        System.out.println("Encrypted: "+enc);

        String dec = decrypt(enc, size);
        System.out.println("Decrypted: "+dec);
    }

    public static String encrypt(String plaintext, int size) {

        StringBuilder sb = new StringBuilder();
        for(int i=0; i<size; i++) {
            sb.append(alphabet[rand.nextInt(alphabet.length)]);
        }
        String initVector = sb.toString();

        String firstPass = shift(plaintext, initVector, false);

        return shift((initVector+firstPass), KEY, false);
    }

    public static String decrypt(String encrypted, int size) {
        String firstPass = shift(encrypted, KEY, true);
        return shift(firstPass.substring(size), firstPass.substring(0, size), true);
    }

    private static String shift(String clear, String shifter, boolean backward) {

        while (shifter.length() < clear.length())
            shifter += shifter;
        shifter = shifter.substring(0, clear.length());

        StringBuilder sb = new StringBuilder();
        for(int i=0; i<clear.length(); i++) {
            sb.append(shift(clear.charAt(i), shifter.charAt(i), backward));
        }
        return sb.toString();
    }

    private static char shift(char c, char s, boolean backward) {
        int indexC = indexOf(c);
        int indexS = indexOf(s);

        int indexShifted;
        if(backward) {
            indexShifted = ((indexC - indexS) + alphabet.length) % alphabet.length;
        } else {
            indexShifted = (indexC + indexS) % alphabet.length;
        }

        return alphabet[indexShifted];
    }

    private static int indexOf(char c) {
        int index = -1;
        for(int i=0; i<alphabet.length; i++) {
            if(c == alphabet[i]) {
                index = i;
                break;
            }
        }
        return index;
    }
}
\$\endgroup\$
1
\$\begingroup\$

Lacking a good reason to do otherwise, I think I'd use a Vernam cipher instead of a Vigenere. The two offer about the same level of security (none, at least against anybody who knows anything about cryptanalysis), but a Vernam cipher is generally easier to implement, and (probably more importantly, at least for your purposes) with a typical key, a single pass produces output that looks much more random (in particular, it frequently produces a lot of relatively unreadable symbols and such. For example:

#include <iostream>
#include <sstream>

int main() {
    std::istringstream input("This is some input for encryption.");
    std::string key("EncryptionKey");

    unsigned pos = 0;
    char ch;
    while (input.get(ch)) {
        std::cout << char(ch ^ key[pos]);
        ++pos;
        if (pos > key.length())
            pos = 0;
    }
}

Obviously, the code is quite short and simple. The result it produces in this case is:

◄♠
☺Y↓I∟☺& Yi+▲▬♠Y▬←←O♂%♠♂y5→
↔↨^

As already noted, there's little difference in terms of real security--but in terms of producing output that looks strange and off-putting, this seems to me to win by a wide margin.

If you were going to mix two different ciphers, I don't think I'd use the same algorithm, just with two different keys. Rather, I'd use two entirely different types of ciphers. Since Vigenere is a substitution cipher, the obvious choice would be to use a transposition cipher for the second one.

\$\endgroup\$
  • \$\begingroup\$ Well, actually the Vernam IS a Vigenere, the only difference is on the key length. And since your key is shorter than the message it's actually a Vigenere. Furthermore if you don't change the key (that's a requirement) the message will be encrypted in the same way. And the random symbols are there only because you have not limited the characters, but I have to provide the encrypted text with a keyboard, so I cannot use all the Unicode here. :) \$\endgroup\$ – Enrichman Jun 3 '16 at 23:38
  • \$\begingroup\$ @Enrichman: From a purely theoretical viewpoint, you're entirely right. From a practical one, however, differences in the code and the result are clear and obvious (and that's likely to remain for all the inputs/keys he cares about). \$\endgroup\$ – Jerry Coffin Jun 4 '16 at 1:28

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.