I was looking at the Vigene Ciphere source code provided on http://rosettacode.org/wiki/Vigen%C3%A8re_cipher#Java. I tried testing out the program myself, and it wasn't outputting the values I expect based on vigene. For example 'dog' being the word and 'bob' being the key I would expect this to be encrypted to 'ech', but is 'qot' instead.
public static void main(String[] args) {
String key = "bob";
String ori = "dog";
String enc = encrypt(ori, key);
System.out.println(enc);
}
static String encrypt(String text, final String key) {
String res = "";
text = text.toLowerCase();
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;
}
However the output is different. Is this because my understanding of the cipher is incorrect or this has taken a different approach to the well known vigenere cipher.
As what was already pointed out by the user, you should change the line to:
res += (char)((c + key.charAt(j) - 2 * 'a') % 26 + 'a');
Alternatively, you can change this:
if (c < 'a' || c > 'z') continue;
to this:
if (c < 'A' || c > 'Z') continue;
Just ensure when you convert the ASCII back to letters, you are using the correct ASCII value (i.e. 65 (A) for uppercase, 97 (a) for lowercase).
Because you are setting your text to encrypt to lowercase, try changing these character literals to lowercase as well:
res += (char)((c + key.charAt(j) - 2 * 'a') % 26 + 'a');
When casting an int to a char, you have to take into account that the integer value of 'a' is not equal to 'A'. Because you are checking that your current character is between 'a' and 'z' (as you have set it to lowercase), you should also have an output in lowercase as well.
Related
So I am doing some practice problems for an upcoming exam and one of the problems is posing a bit of a challenge to me.
The problem states that our code should take a string that has been encoded and decode it. It must work as follows:
Each letter is decoded using the letter immediately before it in the alphabet ("b" becomes "a", "c" becomes "b" ect.)
"a" becomes "z".
each digit works the same way, 8 becomes 7, 5 becomes 4.
0 becomes 9.
characters neither letters nor digits are unchanged.
THE ONLY JAVA METHOD I CAN USE IS IO
Ex:
NFFU NF BU 23 JO UIF CFMM UPXFS
meet me at 12 in the bell tower
heres my current code, i cannot decide whether to use for loops or not. TBH I am not really sure how to tackle this.
public class prb1 {
public static void main(String[] args) {
char letter[]={'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'};
int num[]={0,1,2,3,4,5,6,7,8,9};
System.out.println("Enter Message");
String mssg=IO.readString();
for(char i=0; i<letter.length; i++){
System.out.print(letter[i]--);}
for(int j=0; j<num.length; j++){
System.out.print(num[j]--);
}
}
}
The basic is like this. But this doesn't account yet for the a => z conversion.
String cypher ="ABCDEF";
String plain = "";
for (char c : cypher.toCharArray())
plain += (char) (c - 1);
System.out.println(plain);
With the modulo A => Z, it looks like this:
int A = 'A';
plain += (char) (((c - A - 1 + 26) % 26) + A);
The +26 is needed because java says -1 % 26 == -1 instead of 25.
So this just works for A-Z, but you can easily modify it to work for wider ranges.
You can use a loop to iterate through each character of the message and subtract one from its ascii code if its not a space, an a, or an A:
String message = "NFFU NF BU 23 JO UIF CFMM UPXFS";
String result = "";
for (char thisChar : message.toCharArray()) {
if (thisChar == ' ') {
result += " ";
} else if(thisChar == 'a') {
result += 'z';
} else if (thisChar == 'A') {
result += 'Z';
} else
result += (char)(thisChar - 1);
}
}
System.out.println(result);
Alternatively, you could do:
String message = "NFFU NF BU 23 JO UIF CFMM UPXFS";
String result = "";
for (int i = 0; i < message.length(); i ++) {
char thisChar = message.charAt(i);
if (thisChar == ' ') {
result += " ";
} else if(thisChar == 'a') {
result += 'z';
} else if (thisChar == 'A') {
result += 'Z';
} else
result += (char)(thisChar - 1);
}
}
System.out.println(result);
Technically,
else if(thisChar == 'a') {
result += 'z';
} else if (thisChar == 'A') {
result += 'Z';
}
could be shortened to:
else if(thisChar == 'a' || thisChar == 'A') {
result += (char)(thisChar + 25);
}
Right now I have the following code for a Caesar Cipher, but I run into an issue when I try encrypting text with a very large number.
For example 1000.
static String encrypt(String plaintext) {
StringBuilder ciphertext = new StringBuilder(plaintext);
for (int i = 0; i < ciphertext.length(); i++) {
ciphertext.setCharAt(i, encrypt(ciphertext.charAt(i)));
}
return ciphertext.toString();
}
static String decrypt(String plaintext) {
StringBuilder ciphertext = new StringBuilder(plaintext);
for (int i = 0; i < ciphertext.length(); i++) {
ciphertext.setCharAt(i, decrypt(ciphertext.charAt(i)));
}
return ciphertext.toString();
}
static char encrypt(char c) {
return (char) ('!' + (c - '!' + 1000) % ('~' - '!' + 1));
}
static char decrypt(char c) {
return (char) ('!' + (c - '!' - 1000) % ('~' - '!' + 1));
}
Lets say I input "abc123" into the encryption, using 1000 as my key, I get a bunch of unknown characters. Keep in mind I don't want it to just cycle through a-z but symbols too, using ASCII codes.
Any help would be great!
In Java, the result of modulo is the same sign as the dividend. So when you compute c - '!' - 1000 you will get a negative value and after the modulo it will still be negative. When you add '!', you will have computed a values less than '!', which will be invisible or underflow for a char.
static char decrypt(char c) {
char d = '~' - '!' + 1;
int x = (c - '!' - 1000) % d;
if (x < 0) x += d;
return (char)('!' + x);
}
Here is a discussion for the problem your running into.
How does java do modulus calculations with negative numbers?
I have to implement a variant of the Vigenère cipher. I got the encryption part without issues, but I have a bug in the decryption code and I don't understand what I'm doing wrong.
The requirements are:
the key can only contain A - Z (uppercase)
code values for the key characters are 0 for A, 1 for B, ..., and 25 for Z
do not encode a character if the code is < 32 (preserve control characters)
encrypted character code = original character code + key character code
the final encrypted character must be between 32 and 126, exclusively so if the final encrypted character > 126 it must be brought back into the 32 - 126 range by adding 32 to the value and then subtracting 126
The encryption code:
// it works ok
// I have tested it with some provided strings and the results are as expected
public String encrypt(String plainText)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < plainText.length(); i++) {
char c = plainText.charAt(i);
if (c >= 32) {
int keyCharValue = theKey.charAt(i % theKey.length()) - 'A';
c += keyCharValue;
if (c > 126) {
c = (char) (c + 32 - 126);
}
}
sb.append(c);
}
return sb.toString();
}
The decryption code:
// there probably is an off-by-one error somewhere
// everything is decrypted ok, except '~' which gets decrypted to ' ' (space)
public String decrypt(String cipherText)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cipherText.length(); i++) {
char c = cipherText.charAt(i);
if (c >= 32) {
int keyCharValue = theKey.charAt(i % theKey.length()) - 'A';
c -= keyCharValue;
if (c < 32) {
c = (char) (c + 126 - 32);
}
}
sb.append(c);
}
return sb.toString();
}
Example (with key ABCDEFGHIJKLMNOPQRSTUVWXYZ):
original ~~~~~~~~~~~~~~~~~~~~~~~~~~
encrypted ~!"#$%&'()*+,-./0123456789
decrypted ~ ('~' followed by spaces)
EDIT:
Here is the code I use for testing (it tests every character from 0 to 126 repeated as a string):
public static void main(String[] args) {
int passed = 0;
int failed = 0;
String key = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int c = 0; c <= 126; c++) {
StringBuilder sbString = new StringBuilder();
for (int i = 0; i <= 25; i++) {
sbString.append((char) c);
}
String original = sbString.toString();
Cipher cipher = Cipher(key);
String encrypted = cipher.encrypt(original);
String decrypted = cipher.decrypt(encrypted);
if (!original.equals(decrypted)) {
failed++;
System.out.println("--FAILED--");
System.out.println(original);
System.out.println(encrypted);
System.out.println(decrypted);
} else {
passed++;
}
}
int tests = passed + failed;
System.out.println(tests + " tests");
System.out.println("passed: " + passed);
System.out.println("failed: " + failed);
}
I believe the If(c < 32) in the decryption needs to be If (c <= 32).
Reasoning: if you take the case of Char(126) or '~' then add one to it in the encryption you get 127, which goes through the encryption transform and becomes 33.
When decrypting that you get 33 minus that same 1 which leaves you with 32, which won't trigger the special decryption case. By including 32 in that statement it will trigger the special decryption and change the 32 (" ") to 126 ("~")
You are correct it is an off by one error, but it is kinda subtle
EDIT: there is a collision error because char(32) and char(126) are hashing to the same value. In my previous example that value would be 33, the equation needs to be changed such that Char(126) will hash to 32.
Changing the c = (char) (c + 32 - 126); to c = (char) (c + 32 - 127); should free up the extra space to prevent the collision from happening. THe decrypt will also have to be changed from c = (char) (c + 126 - 32); to c = (char) (c + 127 - 32);
And someone posted that in my comments.
When i encode my byte array using Base64.encode(bytearray, Base64.DEFAULT) so result of this method will add 10 at last item in the resulting byte array and when i converting this resulting byte array into string than 10 will convert into \n(line feed) at the end
please let me know why the \n will append at the end
below is the code that will convert the string into byte array
int inLength = hexValue.length();
int i, o = 0;
long outByte = 0;
byte[] outBytes = new byte[(inLength / 2)];
for (i = 0; i < inLength; i++) {
char c = hexValue.charAt(i);
int value = -1;
if (c >= '0' && c <= '9')
value = (c - '0');
else if (c >= 'A' && c <= 'F')
value = 10 + (c - 'A');
else if (c >= 'a' && c <= 'f')
value = 10 + (c - 'a');
if (value >= 0) {
if (i % 2 == 1) {
outBytes[o++] = (byte) ((outByte << 4) | value);
outByte = 0;
} else {
outByte = value;
}
} else {
if (o != 0)
break;
}
}
return outBytes;
I can't comment right now so i had to leave a reply.
Is this code written by you? or Someone else?
For Base64 encoding, byte are processed in blocks of 3 bytes at a time. If the length of the array is not a multiple of three then either one or two '0' zero bytes is appended to make full block.
And why are you writing your own logic where there are some API available to do the work for you?
Update: I just run the code again with help of API.
Input String: 51b034267f00000144495444
And
Corresponding Base64 encoded String: NTFiMDM0MjY3ZjAwMDAwMTQ0NDk1NDQ0
Simply put, 3 bytes will result in 4 plain text encoded bytes.
Problem one: Here are two code spinner. Code A runs wrong. But I do not know what is wrong.
Problem two: code B is right.but I do not understand why it need to delete 'A’. then add 'A' after fmod. What is the effect about 'A'? Why it has the error after delete?
Code A (ch + key) % 26 )
Code B ('A' + ((ch -'A' + key) % 26))
public void run() {
setFont("Arial-PLAIN-24");
String line = readLine ("Enter line: ");
int key = readInt ("Enter key: ");
String siphertext = encryptCaesar(line , key);
println("The result is: " + siphertext);
String newplain = encryptCaesar(siphertext , -key);
println("newplain:" + newplain);
}
private String encryptCaesar(String str , int key){
if(key < 0){
key = 26 - ( -key % 26 );
}
String result = "";
for(int i = 0; i < str.length(); i++){
char ch = str.charAt(i);
result += encryptChar(ch,key);
}
return result;
}
private char encryptChar(char ch, int key){
if(Character.isUpperCase(ch)){
return ( (char) ('A' + ((ch -'A' + key) % 26)) );
}
return ch;
}
'A' is added to make sure the result of "encryptChar" method, is a valid character in ASCII range 64 to 90, which is A (CAPITAL) to Z (CAPITAL). Refer the ASCII table here.
In your code subtracting of 'A' can also be ignored. That is the below will also work,
('A' + ((ch + key) % 26))
15.7.3 Remainder Operator %
...
It follows from this rule that the result of the remainder operation
can be negative only if the dividend is negative, and can be positive
only if the dividend is positive.
An example is then provided:
int e = (-5)%3; // -2
int f = (-5)/3; // -1
System.out.println("(-5)%3 produces " + e +
" (note that (-5)/3 produces " + f + ")");
If the result of ((ch -'A' + key) % 26)) is negative, then wouldn't the result of (char) ('A' + ((ch -'A' + key) % 26)) be some non-alphabet character? Perhaps you need to add 26 to any negative values or find the absolute value, so that they're positive and result in actual alphabet characters.