问题
I have this simple function for encrypting strings via Vigenère in Java. I omitted the decryption as this is just a "-" instead of the "+" in the line where the new value is calculated.
But this function works only for the normal alphabet A-Z. How can I change the function so that it supports lowercase letters as well as uppercase letters and all other UTF-8 chars?
public static String vigenere_encrypt(String plaintext, String key) {
String encryptedText = "";
for (int i = 0, j = 0; i < plaintext.length(); i++, j++) {
if (j == key.length()) { j = 0; } // use key again if end reached
encryptedText += (char) ((plaintext.charAt(i)+key.charAt(j)-130)%26 + 65);
}
return encryptedText;
}
Thank you very much for your help!
回答1:
Another answer, that does do the Vigenere cipher on upper & lower case characters, simply inserting the other characters. Use this technique to create multiple groups of characters to encode.
public static String vigenere(String plaintext, String key, boolean encrypt) {
final int textSize = plaintext.length();
final int keySize = key.length();
final int groupSize1 = 'Z' - 'A' + 1;
final int groupSize2 = 'z' - 'a' + 1;
final int totalGroupSize = groupSize1 + groupSize2;
final StringBuilder encryptedText = new StringBuilder(textSize);
for (int i = 0; i < textSize; i++) {
final char plainChar = plaintext.charAt(i);
// this should be a method, called for both the plain text as well as the key
final int plainGroupNumber;
if (plainChar >= 'A' && plainChar <= 'Z') {
plainGroupNumber = plainChar - 'A';
} else if (plainChar >= 'a' && plainChar <= 'z') {
plainGroupNumber = groupSize1 + plainChar - 'a';
} else {
// simply leave spaces and other characters
encryptedText.append(plainChar);
continue;
}
final char keyChar = key.charAt(i % keySize);
final int keyGroupNumber;
if (keyChar >= 'A' && keyChar <= 'Z') {
keyGroupNumber = keyChar - 'A';
} else if (keyChar >= 'a' && keyChar <= 'z') {
keyGroupNumber = groupSize1 + keyChar - 'a';
} else {
throw new IllegalStateException("Invalid character in key");
}
// this should be a separate method
final int cipherGroupNumber;
if (encrypt) {
cipherGroupNumber = (plainGroupNumber + keyGroupNumber) % totalGroupSize;
} else {
// some code to go around the awkward way of handling % in Java for negative numbers
final int someCipherGroupNumber = plainGroupNumber - keyGroupNumber;
if (someCipherGroupNumber < 0) {
cipherGroupNumber = (someCipherGroupNumber + totalGroupSize);
} else {
cipherGroupNumber = someCipherGroupNumber;
}
}
// this should be a separate method
final char cipherChar;
if (cipherGroupNumber < groupSize1) {
cipherChar = (char) ('A' + cipherGroupNumber);
} else {
cipherChar = (char) ('a' + cipherGroupNumber - groupSize1);
}
encryptedText.append(cipherChar);
}
return encryptedText.toString();
}
Again, this is unsafe code as the cipher used has been broken for ages. Don't use too many 'A' characters in your keys :) But the character encoding should be sound.
回答2:
Well, you asked for it and I felt like puzzling, but print out the cipher text and you will know what you just asked for...
public static String vigenereUNICODE(String plaintext, String key, boolean encrypt) {
final int textSize = plaintext.length();
final int keySize = key.length();
final StringBuilder encryptedText = new StringBuilder(textSize);
for (int i = 0; i < textSize; i++) {
final int plainNR = plaintext.codePointAt(i);
final int keyNR = key.codePointAt(i % keySize);
final long cipherNR;
if (encrypt) {
cipherNR = ((long) plainNR + (long) keyNR) & 0xFFFFFFFFL;
} else {
cipherNR = ((long) plainNR - (long) keyNR) & 0xFFFFFFFFL;
}
encryptedText.appendCodePoint((int) cipherNR);
}
return encryptedText.toString();
}
EDIT: Please don't ever use in production code, as I haven't got a clue if the code points can indeed be encoded/decoded. Not all points have been defined, as far as I know, and the standard is a moving target.
回答3:
If full unicode support is not possible and you have to define your list of valid characters, anyway, why not just use a function like this?
public static String vigenere_cipher(String plaintext, String key, boolean encrypt) {
String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.-"; // including some special chars
final int alphabetSize = alphabet.length();
final int textSize = plaintext.length();
final int keySize = key.length();
final StringBuilder encryptedText = new StringBuilder(textSize);
for (int i = 0; i < textSize; i++) {
final char plainChar = plaintext.charAt(i); // get the current character to be shifted
final char keyChar = key.charAt(i % keySize); // use key again if the end is reached
final int plainPos = alphabet.indexOf(plainChar); // plain character's position in alphabet string
if (plainPos == -1) { // if character not in alphabet just append unshifted one to the result text
encryptedText.append(plainChar);
}
else { // if character is in alphabet shift it and append the new character to the result text
final int keyPos = alphabet.indexOf(keyChar); // key character's position in alphabet string
if (encrypt) { // encrypt the input text
encryptedText.append(alphabet.charAt((plainPos+keyPos) % alphabetSize));
}
else { // decrypt the input text
int shiftedPos = plainPos-keyPos;
if (shiftedPos < 0) { // negative numbers cannot be handled with modulo
shiftedPos += alphabetSize;
}
encryptedText.append(alphabet.charAt(shiftedPos));
}
}
}
return encryptedText.toString();
}
This should be a very short and working version. And the alphabet can easily be stored in a string that can always be extended (which results in different ciphertexts).
来源:https://stackoverflow.com/questions/10280637/vigen%c3%a8re-cipher-in-java-for-all-utf-8-characters