Encrypting with large numbers, using a Caesar Cipher - java

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?

Related

Netbeans Ascii unicode char not working base64 encoding decoding

I'm customized base64 algorithm and it working properly in cmd.
In this algorithm changed some characters and other stuff for research purpose.
I was find original base64 algorithm in following link
https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64
so new customize code is working properly in command prompt but
when start code in netbeans GUI it throw error because of unicode characters.
Why ascii unicode is not working in netbeans but working in terminal?
How to use that ascii code value in netbeans GUI?
Following is my base code after some modify.
class CustomeBase64Encode {
//Old Char
//private final static String base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//New Char
private final static String base64chars = "☻♥♦♣♠•◘○♂☼►♫☼‼_MyCustomeCharacters+/";
public static String encode(String s) {
// the result/encoded string, the padding string, and the pad count
String r = "", p = "";
int c = s.length() % 3;
// add a right zero pad to make this string a multiple of 3 characters
if (c > 0) {
for (; c < 3; c++) {
p += "=";
s += "\0";
}
}
// increment over the length of the string, three characters at a time
for (c = 0; c < s.length(); c += 3) {
// we add newlines after every 76 output characters, according to
// the MIME specs
if (c > 0 && (c / 3 * 4) % 76 == 0)
r += "\r\n";
// these three 8-bit (ASCII) characters become one 24-bit number
int n = (s.charAt(c) << 16) + (s.charAt(c + 1) << 8)
+ (s.charAt(c + 2));
// this 24-bit number gets separated into four 6-bit numbers
int n1 = (n >> 18) & 63, n2 = (n >> 12) & 63, n3 = (n >> 6) & 63, n4 = n & 63;
// those four 6-bit numbers are used as indices into the base64
// character list
r += "" + base64chars.charAt(n1) + base64chars.charAt(n2)
+ base64chars.charAt(n3) + base64chars.charAt(n4);
}
return r.substring(0, r.length() - p.length()) + p;
}
public static String decode(String s) {
s = s.replaceAll("[^" + chars64 + "=]", "");
String p = (s.charAt(s.length() - 1) == '=' ?
(s.charAt(s.length() - 2) == '=' ? "☻☻" : "☻") : "");
String r = "";
s = s.substring(0, s.length() - p.length()) + p;
for (int c = 0; c < s.length(); c += 4) {
int n = (chars64.indexOf(s.charAt(c)) << 18)
+ (chars64.indexOf(s.charAt(c + 1)) << 12)
+ (chars64.indexOf(s.charAt(c + 2)) << 6)
+ chars64.indexOf(s.charAt(c + 3));
r += "" + (char) ((n >>> 16) & 0xFF) + (char) ((n >>> 8) & 0xFF)
+ (char) (n & 0xFF);
}
// remove any zero pad that was added to make this a multiple of 24 bits
return r.substring(0, r.length() - p.length());
}
}

Vigenere Cipher output

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.

Decoding sentences in Java using arrays

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);
}

Vigenère cipher implementation

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.

The error about string encryption scheduling : (char) (ch + key) % 26

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.

Categories

Resources