此代码要求用户输入包含小写字符a的消息,然后将其加密为Vigenere密码,并解密密码以证明反向查找有效。
package com.testing;
import java.util.Scanner;
/**
* A Vigenere Square or Vigenere table consists of the alphabet written out 26
* times in different rows, each alphabet shifted cyclically to the left
* compared to the previous alphabet, corresponding to the 26 possible Caesar
* ciphers, At different points in the encryption process, the cipher uses a
* different alphabet from one of the rows. The alphabet used at each point
* depends on a repeating keyword.
*/
final class VigenereSquare {
/**
* A 2D char array representing the shifted alphabets.
*/
public static final char[][] SQUARE = fillSquare();
private static final int LETTERS_IN_ALPHABET = 26;
private static final int ASCII_RANGE = 256;
private VigenereSquare() {}
/**
* Fill square with shifted alphabets in ASCII positions:
* 'a' = 97 .. 'z' = 122
* @return initialised char[][]
*/
private static char[][] fillSquare() {
char[][] square = new char[ASCII_RANGE][ASCII_RANGE];
int start = 'a';
int end = start + (LETTERS_IN_ALPHABET - 1);
int index = start;
for (int i = start; i <= end; i++) {
for (int j = start; j <= end; j++) {
//Check index position if beyond the range of the alphabet
//reset index position to start.
if (index > end) {
index = start;
}
square[i][j] = (char) index;
index++;
}
index = i + 1;
}
return square;
}
}
/**
* The person sending the message to be encrypted (eg. attackatdawn) chooses a
* keyword and repeats it until it matches the length of the plaintext, for
* example, the keyword lemon, the cipher key will be lemonlemonle.
*/
class CipherKey {
/**
* CipherKey String value.
*/
public final String KEY;
public CipherKey(String text, String keyword) {
KEY = createKey(text, keyword);
}
/**
* Creates a key string of the same length of the text based on
* the keyword.
* @param text to be encrypted
* @param keyword the chosen keyword
* @return the key string
*/
private String createKey(final String text, final String keyword) {
StringBuilder key = new StringBuilder();
for (int i = 0, keywordIndex = 0; i < text.length(); i++,
keywordIndex++) {
if (keywordIndex >= keyword.length()) {
keywordIndex = 0;
}
key.append(keyword.charAt(keywordIndex));
}
return key.toString();
}
}
/**
* Using a VigenereSquare and a CipherKey each row starts with a key letter. The
* remainder of the row holds the letters A to Z (in shifted order). Although
* there are 26 key rows shown, you will only use as many keys (different
* alphabets) as there are unique letters in the key string, here just 5 keys,
* {L, E, M, O, N}. For successive letters of the message, we are going to take
* successive letters of the key string, and encipher each message letter
* using its corresponding key row. Choose the next letter of the key, go along
* that row to find the column heading that matches the message character; the
* letter at the intersection of [key-row, msg-col] is the enciphered letter.
*
* For example, the first letter of the plaintext, A, is paired with L, the
* first letter of the key. So use row L and column A of the Vigenere square,
* namely L. Similarly, for the second letter of the plaintext, the second
* letter of the key is used; the letter at row E and column T is X. The rest
* of the plaintext is enciphered in a similar fashion.
*
* Plaintext: ATTACKATDAWN
* Key: LEMONLEMONLE
* Ciphertext: LXFOPVEFRNHR
*/
final class VigenereCipherEncrypter {
private VigenereCipherEncrypter() {}
/**
* Encrypt the message using the provided CipherKey and VigenereSquare.
* @param message to be encrypted
* @param key used to encrypt message
* @return encrypted message string
*/
public static String encrypt(final String message, final CipherKey key) {
StringBuilder cipher = new StringBuilder();
String k = key.KEY;
char[][] square = VigenereSquare.SQUARE;
for (int i = 0; i < k.length(); i++) {
//Use the integer values of the key and message char at postion i
//to determine which character to use from the VigenereSquare and
//append it to the cipher text.
cipher.append(square[k.charAt(i)][message.charAt(i)]);
}
return cipher.toString();
}
}
/**
* Using ciphered text, a CipherKey and a VigenereSquare the
* VigenereCipherDecrypter achieves decryption by going to the row in the table
* corresponding to the key, finding the position of the ciphertext letter in
* this row, and then using the column's label as the plaintext. For example,
* in row L (from LEMON), the ciphertext L appears in column A, which is the
* first plaintext letter. Next we go to row E (from LEMON), locate the
* ciphertext X which is found in column T, this T is the second plaintext
* letter.
*/
final class VigenereCipherDecrypter {
private VigenereCipherDecrypter() {}
/**
* Decrypt the cipher text using the provided CipherKey and
* VigenereSquare.
* @param cipher text.
* @param key used to decrypt the cipher text.
* @return decrypted message.
*/
public static String decrypt(final String cipher, final CipherKey key) {
StringBuilder message = new StringBuilder();
String k = key.KEY;
char[][] square = VigenereSquare.SQUARE;
for (int i = 0; i < k.length(); i++) {
int rowIndex = k.charAt(i);
char[] row = square[rowIndex];
int colIndex = new String(row).indexOf(cipher.charAt(i));
message.append((char) colIndex);
}
return message.toString();
}
}
/**
* This program asks the user to enter a message to encrypt and a keyword. Based
* on these it will then use a CipherKey and a VigenereSquare. These are then
* used to encrypt the message using a VigenereCipherEncrypter.
*
* Decryption is also performed using a VigenereCipherDecrypter.
*/
public class Vigenere {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("Enter message to encrypt (a-z characters only): ");
String message = in.nextLine();
System.out.println("Enter the keyword: ");
String keyword = in.nextLine();
CipherKey cipherKey = new CipherKey(message, keyword);
String cipherText = VigenereCipherEncrypter.encrypt(message, cipherKey);
System.out.println("Encrypted message: " + cipherText);
String decryptedMessage = VigenereCipherDecrypter.decrypt(cipherText,
cipherKey);
System.out.println("Decrypted message: " + decryptedMessage);
}
}发布于 2015-05-05 18:23:39
int start = 'a'; int end = start + (LETTERS\_IN\_ALPHABET - 1); int index = start; for (int i = start; i <= end; i++) { for (int j = start; j <= end; j++) { //Check index position if beyond the range of the alphabet //reset index position to start. if (index > end) { index = start; } square[i][j] = (char) index; index++; } index = i + 1; }
此代码的目的似乎是填写与字母表中的字母对应的正方形部分。字母表的start总是定义为'a',但是您可以从start和LETTERS_IN_ALPHABET中计算end。然后使用start和end作为常量。为什么不让它们成为常量,然后去掉LETTERS_IN_ALPHABET呢?
private static final char ALPHABET_START = 'a';
private static final char ALPHABET_END = 'z';然后我们就可以用这些:
for (int i = ALPHABET_START; i <= ALPHABET_END; i++) {
char c = (char) i;
for (int j = ALPHABET_START; j <= ALPHABET_END; j++) {
if (c > ALPHABET_END) {
c = ALPHABET_START;
}
square[i][j] = c;
c++;
}
}这比原来的更灵活,因为我们可以通过常量来改变开始和结束。
还请注意,index实际上不是一个索引。这是一封信,所以要么叫letter,要么叫c。
由于我们的新c变量从未在i循环之外使用,并且每次迭代都会被重置,所以只需在循环中定义它。将它从末尾移到开头意味着我们不再需要将其设置为i + 1,因为开始是在i增量之后。在循环的第一次迭代中,c被设置为ALPHABET_START,就像在原始代码中一样。
我们还将c更改为char而不是int,因为这允许在分配给square[i][j]时直接使用它,而只需要在j循环之外进行强制转换。
这个注释现在没有必要了,因为代码读起来就像注释一样。如果c超过了字母表的末尾,请将c重置为字母表的开头。
考虑举一个例子。
* For an ALPHABET_START of 'a' and an ALPHABET_END of 'c', generate
* abc
* bca
* cab这样就更容易看出进展是有意的,而不是偶然的。
StringBuilder key = new StringBuilder(); for (int i = 0, keywordIndex = 0; i < text.length(); i++, keywordIndex++) { if (keywordIndex >= keyword.length()) { keywordIndex = 0; } key.append(keyword.charAt(keywordIndex)); }
这里要做的第一件事是给StringBuilder一个初始容量。我们知道长度所以告诉密码。
StringBuilder key = new StringBuilder(text.length());这允许编译器在开始时分配正确的StringBuilder长度,而不是选择任意长度并根据需要展开。
这段代码的编写方式类似于前面的代码编写方式,但是它所做的事情有所不同。它所做的就是将keyword附加到key,直到它与text的长度相同为止。所以就这么做吧。
final int fullCount = text.length() / keyword.length();
for (int i = 0; i < fullCount; i++) {
key.append(keyword);
}
final int remainingLength = text.length() % keyword.length();
key.append(keyword.substring(0, remainingLength));我们不逐个添加字符,而是追加字符串的整个副本。这就省去了维护keywordIndex的问题。
在Java中,标准的做法是将每个类放在自己的文件中。这使得重用类变得更容易,因为您可以只复制所需的文件。
我用测试用例和其他几个测试用例测试了这段代码:
abc de abcd ef abc gfed
它返回与第一个用例和测试用例的代码相同的结果。我没有根据您的代码检查其他代码,因为我在修改之后想到了它们。它们都产生合理的输出,并与原来的字符串相呼应。
这是一个支持发布的单元测试的论点。如果你已经测试了很多这样的情况,我可以用你的测试。那么,我可以相当肯定的是,这两个版本都做了相同的事情。这使得修改变得更容易,并且有信心它们不会导致回归。
注意:我没有评论这种加密方法。好的?坏的?我不是该说的人。我的评论主要是为了可读性,对性能略加赞赏。
https://codereview.stackexchange.com/questions/88864
复制相似问题