Caesar Cipher decryption not working for non-alphabetical characters - java

I'm completely new to programming and have been tasked with writing a method in Java to decrypt a message encrypted using a Caesar Cipher (without importing any utilities).
The following code was provided for encrypting a message:
public String encrypt(String plainText, int offset) {
String cipher = "";
char[] arr = plainText.toCharArray();
for (int i = 0; i < arr.length; i++) {
int numericalVal = (int) arr[i];
if (Character.isUpperCase(arr[i])) {
cipher += (char) (((numericalVal + offset - 65) % 26) + 65);
} else if (numericalVal == 32) {
cipher += arr[i];
}
else {
cipher += (char) (((numericalVal + offset - 97) % 26) + 97);
}
}
return cipher;
My solution must begin with the line Public String decrypt(String plainText, int offset) {
This is how I attempted to solve the problem:
public String decrypt(String plainText, int offset) {
String decipher = "";
char[] d_arr = plainText.toCharArray();
for (int i = 0; i < d_arr.length; i++) {
int numericalVal = (int) d_arr[i];
if (Character.isUpperCase(d_arr[i])) {
decipher += (char) ((((numericalVal - offset - 65) % 26 + 26) % 26) + 65);
//to get remainder for negative values too
} else if (numericalVal == 32) {
decipher += d_arr[i];
}
else {
decipher += (char) ((((numericalVal - offset - 97) % 26 + 26) % 26) + 97);
}
}
return decipher;
This works when decrypting letters of the alphabet, but non-alphabetical letters are not decrypted properly and I am unsure what the issue is.
For example:
public static void main(String[] args) {
CaesarCipher C = new CaesarCipher();
System.out.println(C.encrypt("?", 4)); //returns the ] symbol
System.out.println(C.decrypt("]", 4)); //returns the letter s
We were told that adjusting the code to ignore non-alphabetical characters entirely was possible but would require more work, so I changed the } else if (numericalVal == 32) { cipher += arr[i]; code in both the encrypt and decrypt Strings to } else if (numericalVal < 65 || (numericalVal > 90 && numericalVal < 97) || numericalVal > 122) { cipher += arr[i];.
This circumvented the issue but I was told that it's much easier to just decrypt the non-alphabetical characters as well, so I reverted this change, but now I'm at a complete loss as to how to solve this problem. I feel like I'm missing something very simple as I managed to do it "the hard way" but cannot do it the easier way. I can see that when a non-alphabetical value is encrypted, the alphabetical letters are essentially skipped, but the same is not occurring for the decryption process. I presume this is related to the adjustment I made to find the remainder of negative values, but I am unsure.

What you generally try and do is to define your own alphabet rather than using the ABC, and put that in a string (or a char array). Then you replace the - 65 (which is not very great when it comes to encoding, you could have used e.g. just - 'A') by looking up the character in the alphabet.
Then you can perform the modulus operation on the size of the alphabet, i.e. alphabet.length() for strings or alphabet.length for char arrays. Then you perform the modular addition / subtraction, and finally you find the corresponding character in your alphabet again.
Now you have some special code for space and upper / lowercase. That would not work anymore when you'd include special characters. There are two ways around this. The simplest one is to create one big alphabet with uppercase, lowercase and special characters. If you want to keep the case you could also use e.g. 3 separate alphabets.
So you start off with e.g.
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,.;";
or you could use:
private static final String ALPHABET_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String ALPHABET_LOWER = "abcdefghijklmnopqrstuvwxyz";
private static final String ALPHABET_SIGNS = ",.;";
I'd say the single alphabet is a bit more secure as you would directly be able to see the signs in the other. Then again, the Caesar cipher was only somewhat secure when almost nobody was able to read in the first place.
Better split your application into methods, have at least:
charToIndex(char c): int;
indexToChar(int i): char;
shiftIndex(int i, int shift): int.
and while we are at it:
public static int mod(int i, int n) {
return ((i % n) + n) % n;
}

Related

Too many characters in character literal error

I'm creating a stylish text app but on some places I'm getting an error ("Too many characters in character literal"). I am writing only one letter but when I paste it converts into many letters like this: "\uD83C\uDD89" and the original letter is "🆉".
Please tell me how to write this in a correct way.
for (int charOne = 0; charOne <= strBld.length() - 1; charOne++) {
char a = strBld.charAt(charOne);
char newCh = getSpecialCharEighth(a);
strBld.setCharAt(charOne, newCh);
}
private char getSpecialCharEighth(char a) {
char ch = a;
if (ch == 'Z' || ch == 'z') {
ch = '\uD83C\uDD89';
}
return ch;
}
A Java char stores a 16-bit value, i.e. can store 65536 different values. There are currently 137929 characters in Unicode (12.1).
To handle this, Java strings are stored in UTF-16, which is a 16-bit encoding. Most Unicode characters, known as code points, are stored in a single 16-bit value. Some are stored in a pair of 16-bit values, known as surrogate pairs.
This means that a Unicode character may be stored as 2 char "characters" in Java, which means that if you want your code to have full Unicode character support, you can't store a Unicode character in a single char value.
They can be stored in an int variable, where the value is then referred to as a code point in Java. It is however often easier to store them as a String.
In your case, you seem to be replacing Unicode characters, so a regex replacement call might be better, e.g.
s = s.replaceAll("[Zz]", "\uD83C\uDD89");
// Or like this if source file is UTF-8
s = s.replaceAll("[Zz]", "🆉");
UPDATE
If you want to keep a method for determining the replacement value, you could do this:
s = Pattern.compile(".").matcher(s).replaceAll​(mr -> getSpecialCharEighth(mr.group()));
private static String getSpecialCharEighth(String s) {
int cp = s.codePointAt(0);
if (cp >= 'A' && cp <= 'Z')
return Character.toString​(cp - 'A' + 0x1f170); // "🅰" - "🆉"
if (cp >= 'a' && cp <= 'z')
return Character.toString​(cp - 'a' + 0x1f170); // "🅰" - "🆉"
return s;
}
Note: replaceAll​(replacer) is Java 9+ and Character.toString(codePoint) is Java 11+.
UPDATE 2
Since question is tagged android, Java 9 and Java 11 APIs are not available, so here is Java 7+ solution.
StringBuffer buf = new StringBuffer(s.length() + 16);
Matcher m = Pattern.compile(".").matcher(s);
while (m.find())
m.appendReplacement(buf, getSpecialCharEighth(m.group()));
s = m.appendTail(buf).toString();
private static String getSpecialCharEighth(String s) {
int cp = s.codePointAt(0);
if (cp >= 'A' && cp <= 'Z')
return new String(new int[] { cp - 'A' + 0x1f170 }, 0, 1);
if (cp >= 'a' && cp <= 'z')
return new String(new int[] { cp - 'a' + 0x1f170 }, 0, 1);
return s;
}
Result with s = "Hello World!"
🅷🅴🅻🅻🅾 🆆🅾🆁🅻🅳!
You can't do that with char data type. Use String instead.
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).

Trouble with finding odd and even index values for characters

I'm creating simple encryption/decryption software as a fun project. I am able to encrypt. It gives me the encrypted password and the key. The problem I'm having is in the decryption. For an example, here is an encrypted message: eUwG:Q_vul=u^CMiojpnif and here is the key: 103141109141719141119050117050318040907010912. The way it works is by first adding a random salt to the beginning or end of the message which is defined by the first number in the key. So since the first number is 1 then the salt is at the beginning (the salts are 14 characters long), so it removes that text, leaving Miojpnif. In the key after the salt number, there are 2 numbers per letter in the text. which is where I'm stuck. A number 1 means that the character was shifted forward, and 0 means backwards. So for the 'M' in the key for that character it's 0 so it was shifted backwards and the next number is 3 meaning that it was shifted backwards by 3. So to reverse this I need to shift that character forward 3. The thing is, I can't figure out how to make this work properly. My idea is that it removes the first number (salt) from the key, and then if the number is odd, then it records if the character will go forward or back, and if it's even then it'll move that character forward or back (which is stored as an int like the rest) by that number. So where I'm stuck is that figuring out if it's even or odd isn't working properly and I can't quite figure out how to shift that character.
I already looked up how to figure out if it's even or odd, but it still doesn't work. Actually shifting the character I made up my own code for. I don't know if you guys understand what I need help with because I didn't really know how to express it in words. So here is the code that I have, I hope that y'all can help.
for(int i= 0; i < keyNew.length(); i++){
if(i % 2 == 1){
/*odd*/
if(keyNew.charAt(i) == '1') {
forward = 1;
backward = 0;
} else {
forward = 0;
backward = 1;
}
}else{
/*even*/
if(forward == 1 && backward == 0) {
/*forward*/
System.out.println("forward");
String encryptedNewer = encryptedNew.charAt(i / 2) += keyNew.charAt(i);
} else if(forward == 0 && backward == 1) {
*backward*/
System.out.println("backward");
String encryptedNewer = encryptedNew.charAt(i / 2) += keyNew.charAt(i);
}
}
}
encrypted is the encrypted text, key is the key, encryptedNew is the text without the salt and keyNew is the key without the first digit.
This is a great example of the long method smell. I recommend the following:
Work with String and use the substring(...) function. It is easier, because you need less variables and don't have to convert from char to int and back.
Create a function encrypt(...) and decrypt(...) which calls some "subfunctions"
One subfunction is addSalt(...) and removeSalt(...)
One subfunction is splitKeyToPairs(...) which returns a List of strings with 2 Digits per Item.
One subfunction is shiftForward(...) and shiftBackwards(...)
Then I would implement it as follow:
public String decrypt(String key, String cipher) {
String cipherModified = removeSalt(key, cipher);
List<String> keyPairs = splitKeyToPairs(key.substring(1, key.length()));
String message = "";
for(int position = 0; position < keyPairs.size();++position) {
String keyTmp = keyPairs.get(position);
String cipherLetter = cipherModified.substring(position, position + 1);
message += "0".equals(keyTmp.substring(0, 1)) ? shiftBackwards(cipherLetter, keyTmp.substring(1, 2)) : shiftForward(cipherLetter, keyTmp.substring(1, 2));
}
return message;
}
public List<String> splitKeyToPairs(String key) {
List<String> result = new ArrayList<String>();
for(int i = 0; i < key.length(); i += 2) {
//i += 2 because we jump 2 characters per iteration.
result.add(key.substring(i, i+2));
}
return result;
}
Here a little test function for the split but not for the decrypt:
public static void main(String[] args) {
List<String> result = splitKeyToPairs("1234567890");
List<String> test = new ArrayList<>(Arrays.asList("12", "34", "56", "78", "90"));
for(int i = 0; i < result.size();++i) {
if(!result.get(i).equals(test.get(i))) {
System.out.println("error on position " + i);
}
}
}
Ok, here is another approach to decrypting the message. Establishing methods and tucking them away in a library would be advisable. In the following example I omitted the salt digit from the key. The logic to accommodate that is trivial.
String key = "03141109141719141119050117050318040907010912";
String code = "eUwG:Q_vul=u^CMiojpnif";
int i = 0;
StringBuilder sb = new StringBuilder();
for (int pos = 0; pos < code.length(); pos++) {
// get codeLetter
char codeLetter = code.charAt(pos);
// set direction
int direction = key.charAt(i++) == '0' ? 1
: -1;
// set count
int count = key.charAt(i++) - '0';
// modify codeLetter based on direction and count
char c = (char) (codeLetter + (count * direction));
// save it.
sb.append(c);
}
System.out.println(sb.toString().substring(14));

TEA encryption and decryption implementation

I have written both encryption and decryption methods to be able to encrypt plain text or decrypt cipher text. I am unsure how to successfully implement this, however, due to taking in the HEX and text as a STRING and unsure how to convert them to int arrays, and then how to successfully print the result out without it being a bunch of jumbled letters. What am I doing incorrectly?
The following code is the two TEA methods for either encryption or decryption:
public void encrypt (int[] block, int[] key) {
int i = block[0];
int j = block[1];
int sum = 0;
int delta = 0x9e3779b9;
for (int k = 0; k < 32; k++) {
sum += delta;
i += (j << 4 & 0xffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7fffffff)
+ key[1];
j += (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff)
+ key[3];
}
block[0] = i;
block[1] = j;
}
public void decrypt (int[] block, int[] key) {
int i = block[0];
int j = block[1];
int sum = 0;
int delta = 0x9e3779b9;
for (int k = 0; k < 32; k++) {
i -= (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff)
+ key[3];
j -= (j << 4 & 0xfffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7ffffff)
+ key[1];
sum -= delta;
}
block[0] = i;
block[1] = j;
}
and this is the user interface code:
public void encryptionT() {
p.plainText();
String pText = input.next();
p.hexNumber();
String hexNum = input.next();
byte hex[] = hexNum.getBytes();
byte pTextBytes[] = pText.getBytes();
byte[] encryptedPlainBase = Base64.getEncoder().encode(pTextBytes);
System.out.println(encryptedPlainBase);
}
public void decryptionT() {
p.cipherText();
String cText = input.next();
p.hexNumber();
String hexNum = input.next();
byte hex[] = hexNum.getBytes();
byte cTextBytes[] = cText.getBytes();
BigInteger hexBigInt = new BigInteger(1, hex);
BigInteger plainBigInt = new BigInteger(1, cTextBytes);
BigInteger cTextHexResult = hexBigInt.multiply(plainBigInt);
byte[] decryptedCipherText = cTextHexResult.toByteArray();
byte[] decryptedCipherBase = Base64.getDecoder().decode(decryptedCipherText);
System.out.println(decryptedCipherBase);
EDIT:
I attempted to do the suggested edits but I get the following error. I'm not sure if I just didn't understand.enter image description here
Wow, this is the first time I've seen an implementation of encryption / decryption where the actual encryption / decryption is forgotten entirely.
I'll explain about the key first - usually you start with the key, not the plaintext as the key can commonly be reused for other messages. TEA however only accepts a key, not an IV or nonce, so encrypting multiple messages with the same key will break the cipher.
The hexadecimal key must first be decoded to binary. However, you need a hexadecimal decoder for that. This is however not included in the normal Java package java.util - use e.g. the Apache codec library instead. This should leave you with the binary key of half the size you are obtaining now (the binary encoding of the hexadecimal characters).
Now you need to encode the plaintext message to binary - i.e. a byte array in Java, which you are doing in pText.getBytes().
Now you need to create the ciphertext from the plaintext message by encrypting the binary obtained above.
You are correct in seeing that the ciphertext must be encoded if you want to treat it as text. So using a base64 encoding on the result is good practice - unless binary ciphertext would also suffice (Java I/O streams and files are fine with binary values) in which case the encoding is simply not necessary.
Decryption goes the other way except for the key. You first create the key as explained above. Then you decode the base 64, decrypt the binary result and then use the String constructor that takes a byte array to retrieve the plaintext.
Note that String#getBytes and new String(byte[]) use the platform decoding by default. You may want to specify an exact encoding such as UTF-8 to be compatible between platforms.

Getting a character to be represented by an int 0-25

I'm working on a cryptography program that implements various traditional methods. For some of them it is best for my message to be represented as numbers 0-25. A is 0, B is 1, etc. This shift cipher is a case of that since you must take mod 26 for a wrap around. Also spaces and punctuation must be preserved.
Here is the code for the method that does the shift cipher:
public static void shift(char k, char eord)
{
if(eord=='E' || eord=='e')
{
int [] mi= new int[mc.length];
for(int i=0; i<mc.length; i++)
{
if ((mc[i]>='a' && mc[i]<='z') || (mc[i]>='A' && mc[i]<='Z'))
{
mi[i]=(int)(mc[i]+k);
mi[i]=mi[i]%26;
//mc[i]=(char)mi[i];
//System.out.println(mi[i]);
}
}
}
}
mc is an array of characters that holds the message and eord is a char that will determine whether to run the algorithm to encrypt or decrypt. What I have the code do is check to make sure that mc[i] is a letter and then add the char k (the key) and then I type cast it into an integer so I can mod 26. Something does not work correctly because when I have a key of 'b' (1) and see what the integer representation is it is definitely not correct. I also need to convert it back to a character when I'm done so I can give the user the plaintext/cipher text of the message.
To get an integer value of 0..25 of a character you need to make sure that you only get upper- or lowercase characters in the alphabet.
Let's assume lowercase. Then you can simply convert the characters to integer by subtracting the value of the 'a' character. As the character values are ordered as in the normal alphabet, this will give 'a' value of 0 and 'z' value of 25... and all the letters in between will get the correct value as well.
I'll show a lowercase version as I don't like shouting:
public class CharacterToZeroBasedIntegerRange {
public static int characterToIntegerRange(char c) {
if (c < 'a' || c > 'z') {
throw new IllegalArgumentException(String.format("Character with value %04X is not a letter", (int) c));
}
return c - 'a';
}
public static void main(String[] args) {
String test = "Hello world!".toLowerCase().replaceAll("[^a-z]", "");
System.out.println(test);
for (int i = 0; i < test.length(); i++) {
char c = test.charAt(i);
System.out.println(characterToIntegerRange(c));
}
}
}

Calculate checksum fails when strings are differently encoded

I have a routine for checksum calculation and it fails for string which have accented characters, to handle accented characters I convert the string to a byte array using the encoding which the string is actually from, for ex: a string will contains accented characters appearing as François^Frédérique^ and the encoding it uses is 'CP437', and if after doing such the checksum calculation fails, thus can you let me know if there is something more I need to be doing.
byte[] frameArray = frame.getBytes(m_fileEncoding);
int frameLength = frameArray.length;
int idx =0;
while (idx < frameLength )
{
int c = frameArray[idx];
sum1 += c;
if (sum1 >= 256)
{
sum1 -= 256;
}
idx++;
}
sum1 = 256 - sum1;
calculatedChecksum = Integer.toHexString(sum1 & 0xff).toUpperCase();

Categories

Resources