Okay so I created this code last year for a class project and I remember it working correctly. I now need it to implement a text cipher but for some reason it does not work correctly. It will encrypt, but when I try to decrypt only the first two letters are correct. The rest of it is all wrong. It is very simple, it is a command-line program where the first argument is whether it is encrypting(-e) or decrypting(-d), second argument is the key and third argument is the text you will encrypt. It is similar to a caesar cipher except it takes each character as a reference when adding to each individual char in the string. Can anyone tell me what is wrong, I do not understand why it does not work anymore and I need it for a project.
import java.util.*;
public class VigenereCipher
{
public static void main(String[] args)
{
Scanner scan = new Scanner (System.in);
String key = "";
String ori = "";
String res = "";
if(!(args.length == 0))
{
if (args[0].equals("-e"))
{
key = args[1];
ori = args[2];
encrypt(ori, key);
System.out.println(encrypt(ori, key));
}
else if (args[0].equals("-d"))
{
key = args[1];
ori = args[2];
decrypt(ori, key);
System.out.println(decrypt(ori, key));
}
else
{
System.out.print("Usage: java VigenereCipher [-e,-d] key text");
}
}
}
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;
res += (char)((c + key.charAt(j) - 2 * 'A') % 26 + 'A');
j = ++j % key.length();
}
return res;
}
static String decrypt(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;
res += (char) ((c - key.charAt(j) + 26) % 26 + 'A');
j = ++j % key.length();
}
return res;
}
}
You are supposed to be able to encrypt a string of text with a key then decrypt the output from the encryption using the same key for example: java VigenereCipher -e hello hello will give me "UOCCI" as output but when I take that output and do java VigenereCipher -d hello UOCCI it gives me "HE225" as my output and not "HELLO".
You forgot that your key also needs to be in the same alphabet. So if you supply a lowercase key your algorithm will fail.
This will become abundantly clear when you split your algorithm in parts, e.g. I just went through:
int im = c + key.charAt(j) - 2 * 'A';
res += (char)(im % 26 + 'A');
with my debugger and presto, the problem showed up.
Related
However I had an assignment of programming in java related to a text i already have under (text).
the function is supposed to as below
getEncryptedText(int shift)
return a string representation of ciphertext given that the text to be manipulated is the plaintext using Caesar Cipher.
The number of rotation is depend on the shift value;
positive shift value represent the right rotation while negative shift value represent left
rotation. However, unlike explain in Wikipedia, this method used following string as
plain:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Other characters than above will be treated as it is (i.e. will not been encrypted)
*Further reading: https://en.wikipedia.org/wiki/Caesar_cipher
So this is the class method I have made so far and wanted to know how can i keep the text chars which aren't included in the plaintext i have such as "!,#,#,$,%... and so on". So far i tried everything but couldn't make it but the rest seems fine!
public String getEncryptedText(int shift) {
String ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
String cipherText = "";
for (int i = 0; i < text.length(); i++){
{
int charPosition = ALPHABET.indexOf(text.charAt(i));
if(text.charAt(i) == ' ') {
cipherText += " ";
}
else
{
int keyVal = (shift + charPosition) % 62;
char replaceVal = ALPHABET.charAt(keyVal);
cipherText += replaceVal;
}
}
}
return cipherText;
}
Consider modifying your if statement and using the StringBuilder class:
class Main {
public static void main(String[] args) {
CesarCypherHelper cesarCypherHelper = new CesarCypherHelper();
System.out.println(cesarCypherHelper.getEncryptedText("Hello World!", 2));
System.out.println(cesarCypherHelper.getEncryptedText("Hello World!", 64));
}
}
class CesarCypherHelper {
public String getEncryptedText(String text, int shift) {
String ALPHABET =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
StringBuilder encryptedText = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
int charPosition = ALPHABET.indexOf(ch);
if (charPosition == -1) {
encryptedText.append(ch);
} else {
int keyVal = (shift + charPosition) % ALPHABET.length();
encryptedText.append(ALPHABET.charAt(keyVal));
}
}
return encryptedText.toString();
}
}
Output:
Jgnnq Yqtnf!
Jgnnq Yqtnf!
For my CPSC class, I need to make encryption code using caesar cipher. That is done. The next part is taking the encrypted message and cycling the secretKey to be added to the encrypted message. For example, if I encrypt "Hello!" using a shift of 13, it will turn into "Uryyb!". Then I must shift "U" by one, "r" by three, "y" by one, etc... which will encrpt into "Vuzbc!" I am in a beginner class so I do not know all the cool tips and tricks. Only possible solution I know is to take the outcome of the caesar cipher and somehow index the secret key to be added to the outcome.
Here is my code that I have so far:
public class Cipher {
private int secretKey;
private int superSecretKey;
public static void main(String [] args)
{
Cipher cipher = new Cipher(1);
}
public Cipher(int myKey) {
secretKey = myKey;
}
public String caesarEncrpyt (String s) {
String r = "";
for(int i = 0; i < s.length(); i++) {
char c = (char) (s.charAt(i));
if(Character.isLetter(c)) {
if (Character.isUpperCase(c)) {
r += (char) ('A' + (c - 'A' + secretKey) % 26);
}
else {
r += (char) ('a' + (c - 'a' + secretKey) % 26);
}
}
else {
r += c;
}
}
return r;
}
public String caesarDecrypt (String s) {
String r = "";
for(int i = 0; i < s.length(); i++) {
char c = (char) (s.charAt(i));
if(Character.isLetter(c)) {
if (Character.isUpperCase(c)) {
r += (char) ('A' + (c - 'A' - secretKey) % 26);
}
else {
r += (char) ('a' + (c - 'a' - secretKey) % 26);
}
}
else {
r += c;
}
}
return r;
}
public String augustusEncrypt (String s) {
String r = "";
for(int i = 0; i < s.length(); i++) {
char c = (char) (s.charAt(i));
if(Character.isLetter(c)) {
if (Character.isUpperCase(c)) {
r += (char) ('A' + (c - 'A' + secretKey) % 26);
}
else {
r += (char) ('a' + (c - 'a' + secretKey) % 26);
}
}
else {
r += c;
}
}
return r;
}
augustusEncrypt is a copy and paste of caesarEncrypt. I've been moving some stuff around hoping for a solution. Thanks in advance!
Edit: I may not have explained this correctly, if you have a question, I'll be here.
Write a function, call it toDigits which will take an int (or a long) and return an array of ints corresponding to the digits of the input. to toDigits(13)=>{1,3} and toDigits(4834)=>{4,8,3,4}, etc
Then write a function encryptChar, taking a char and an int and encrypting the char by that int. (encryptChar('e', 1)=>'f', encryptChar('a',28)=>c, etc)
Then you can loop over the characters of the message and the digits in this array, passing the values to encryptChar and use the results to assemble your encrypted message. In a loose sort of pseudocode:
fn encryptMessage(message, key):
key_array = toDigits(key)
output = ""
for i in length (message):
output.append(encryptChar(message[i], key_array[i % length(key_array)]))
Best practices and conventions for encrypting aside, the solution is simple.
You have letters A-Z and a-z which already perform the correct loop when we step off of the alphabet, and you believe you have that working correctly. All you need to do is add 1 before you loop around.
It would be something like this (warning: untested):
('A' + ((c+1) - 'A' + secretKey) % 26)
Hello evrybody who reads this!
I need to realize Vigenere cipher on Java.
I have a .txt document, which I'm going to read, encode and decode. Here it is:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
My problem is that I don't know how to shift chars correctly, According to this table latin letters have codes from 0061 to 007A. German ones that I need: 00C0 - 00FF, polish: 0100-017F, russian 0430-044F and I didn't gind chineese.
How can I specify unshiftChar and shiftChar to make it correst?
Now my input looks like this:
The original text from file is:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
String that will be encoded is:
asciiabcdexyzgermanäöüäöüßpolishąęźżńłrussianабвгдежэюяcjk你好
The encrypted string is:
äckkwdfaqmzjökcbucäbdslhwfssjvåjoxfbsltfvwgnvboegbrnboeghxöb
The decrypted phrase is:
asciiab¥dexyzgrmanäöäöuupo○ibmjcåäldhtciwmtdåawmtddpw
Here is a Java code:
public class VigenereCipher
{
public static void main(String[] args) throws IOException
{
String key = "Unicode";
File file = new File("G:\\unicode.txt");
FileInputStream fis = new FileInputStream(file);
byte[] fileBArray = new byte[fis.available()];
fis.read(fileBArray);
String text = new String(fileBArray, "UTF-8");
//String text = "Some simple text to check the decoding algorythm";
System.out.println("The original text from file is: \n" + text);
String enc = encrypt(text, key);
System.out.println(enc + "\n");
System.out.println("The decrypted phrase is: ");
System.out.println(decrypt(enc, key));
}
// Encrypts a string
public static String encrypt(String message, String key)
{
message = StringToLowerCaseWithAllSymbols(message);
System.out.println("String that will be encoded is: \n" + message);
char messageChar, keyChar;
String encryptedMessage = "";
for (int i = 0; i < message.length(); i++)
{
messageChar = shiftChar(message.charAt(i));
keyChar = shiftChar(key.charAt(i % key.length()));
messageChar = (char) ((keyChar + messageChar) % 29);
messageChar = unshiftChar(messageChar);
encryptedMessage += messageChar;
}
System.out.println("\nThe encrypted string is: ");
return encryptedMessage;
}
// Decrypts a string
public static String decrypt(String cipher,String key)
{
char cipherChar, keyChar;
cipher = StringToLowerCaseWithAllSymbols(cipher);
String decryptedMessage = "";
cipher = cipher.toLowerCase();
for (int i = 0; i < cipher.length(); i++)
{
cipherChar = shiftChar(cipher.charAt(i));
keyChar = shiftChar(key.charAt(i % key.length()));
cipherChar = (char) ((29 + cipherChar - keyChar) % 29);
cipherChar = unshiftChar(cipherChar);
decryptedMessage += cipherChar;
}
return decryptedMessage;
}
// Prunes all characters not in the alphabet {A-Öa-ö} from a string and changes it to all lower case.
public static String StringToLowerCaseWithAllSymbols(String s)
{
//s = s.replaceAll("[^A-Za-zåäöÅÄÖ]", "");
// 's' contains all the symbols from my text
s = s.replaceAll("[^A-Za-zäöüÄÖÜßąęźżńłабвгдежэюя你好]", "");
return s.toLowerCase();
}
// Assigns characters a,b,c...å,ä,ö the values 1,2,3...,26,28,29.
private static char shiftChar(char c)
{
if (96 < c && c < 123)
{
c -= 97;
}
else if (c == 229)
{
c = 26;
}
else if (c == 228)
{
c = 27;
}
else if (c == 246)
{
c = 28;
}
return c;
}
// Undoes the assignment in shiftChar and gives the characters back their UTF-8 values.
private static char unshiftChar(char c)
{
if (0 <= c && c <= 25)
{
c += 97;
}
else if (c == 26)
{
c = 229;
}
else if (c == 27)
{
c = 228;
}
else if (c == 28)
{
c = 246;
}
return c;
}
}
First of all, you don't want to shift: You want to rotate. Suppose we're working with the English alphabet. If 'A'+2 is 'C', what's 'Z'+2? When you're implementing a Vigenere cipher, you want 'Z'+2=='B'.
I would would not use Unicode in a Vigenere cipher program: I would use my own encoding in which the first letter of the alphabet is represented by zero, the second letter is represented by one, and so on. So, for my English example, code('A')==>0, code('B')==>, ... code('Z')==>26.
Then my rotation function looks like this:
int rotate(Alphabet alphabet, int code, int amount) {
return (code + amount) % alphabet.getLength();
}
So:
rotate(english, code('A'), 2) ==> (0 + 2)%26 == 2, (the code for 'C'), and
rotate(english, code('Z'), 2) ==> (25 + 2)%26 == 1, (the code for 'B').
I have made a vigenere encryption/decryption program which seems to work as I intended, however running my encryption/decryption on a very large text file (500,000 characters aprox) takes 2-4minutes. I have looked through my code and cannot see what operations might be slowing it down. Anyone have any ideas how I could speed this up?
Code:
public static String encrypt(String text, String key)
{
String cipherText = "";
text = text.toLowerCase();
for(int i = 0; i < text.length(); i++)
{
System.out.println("Count: "+ i); //I just put this in to check the
//loop wasn't doing anything unexpected
int keyIndex = key.charAt(i%key.length()) - 'a';
int textIndex = text.charAt(i) - 'a';
if(text.charAt(i) >= 'a' && text.charAt(i) <= 'z') { //check letter is in alphabet
int vigenere = ((textIndex + keyIndex) % 26) + 'a';
cipherText = cipherText + (char)vigenere;
} else
cipherText = cipherText + text.charAt(i);
}
}
return cipherText;
}
Prior to running the encrypt I have a method which reads the text file to a String using Scanner. This String plus a predefined key are used to create the encrypted text.
Thanks.
ANSWER
Thanks to RC - it was my string concatenation taking the time. If anyone else is interested this is my updated code which works quickly now:
public static String encrypt(String text, String key)
{
StringBuilder cipher = new StringBuilder();
for(int i = 0; i < text.length(); i++)
{
int keyIndex = key.charAt(i%key.length()) - 'a';
int textIndex = text.charAt(i) - 'a';
if(text.charAt(i) >= 'a' && text.charAt(i) <= 'z') {
int vigenere = ((textIndex + keyIndex) % 26) + 'a';
cipher.append((char)vigenere);
} else {
cipher.append(text.charAt(i));
}
}
return cipher.toString();
}
Append to a StringBuilder instead of creating new String instances.
You want to do a
buffer.append((char)vigenere);
instead of a cipherText = cipherText + (char)vigenere;
At present you are doing
for(int i = 0; i < text.length(); i++){
...
int keyIndex = key.charAt(i%key.length()) - 'a';
...
}
You can try to remove the calucation of the keyIndex from the for-loop and realize it in a preprocessing step. For example, you can store the keyIndex values/ characters in a separate array and access the array contents in your original loop. This should save you some calculation steps.
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!
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.
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).
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.