Java UTF-16LE Weird Error That Stumped My Professor

349 views Asked by At

As described above, I'm having an issue with UTF-16LE in Java. When I run my code, it usually encrypts, decrypts, and prints the message correctly. Sometimes it loses two of the encrypted characters and other times it prints out a non-english character such as greek, etc.

This is for a class and it did stump my professor, any help would be greatly appreciated on the error.

/*This program was written by me for my professors class.

The purpose of this program is to encrypt a message using a XOR encryption, decrypt it the same way as well to generate a codebook for those methods.*/

import java.security.*;

public class Crypto 
{
    public static void main(String[] args) throws Exception
    {           
        Crypto c = new Crypto(); 

        byte[]codebook = null;
        String message = "";   

        System.out.println("Generating codebook");
        codebook = c.makeCodebook(14);
        System.out.println();

        System.out.println("Now Encrypting");
        System.out.println("This is the contents of the Encrypted Message: ");
        message = c.crypt("1234567", codebook);
        System.out.println();

        System.out.println("Now Decrypting");
        System.out.println("This is the contents of the Decrypted Message");
        message = c.crypt(message, codebook);

        System.out.println();
        System.out.println("Your message is: ");      
        System.out.println(message);

    }//publis static void main(String [] args)

    //Encrypts or decrypts message against the codebook assuming the appropriate length
    public String crypt (String message, byte [] codebook) throws Exception
    {
        //Take the message and put it into an array of bytes.
        //This array of bytes is what will be XORed against the codebook
        byte[] cryptedMessage = message.getBytes("UTF-16LE");
        byte[] result = new byte[14];
        message = "";
        System.out.println(message.length());
        System.out.println(cryptedMessage.length);
        System.out.println(result.length);
        //Let's see the contents of encryptedMessage
        for(int i = 0; i< cryptedMessage.length; i++)
        {
            System.out.print(cryptedMessage[i]+" ");
        }//for(int i = 0; i< encryptedMessage.length; i++)
        System.out.println();

        //XOR codebook and encryptedMessage
        System.out.println("This is the message using XOR:");
        for(int i = 0; i<result.length; i++)
        {
            //since XOR has return type of an int, we cast it to a byte
            result[i] = (byte)(((byte)(codebook[i])) ^ ((byte)(cryptedMessage[i])));
            System.out.print(result[i]+" ");
        }//while(result[i]!=0)
        //output
        System.out.println();
        //output

        System.out.println(message.length());
        System.out.println(cryptedMessage.length);
        System.out.println(result.length);
        return new String(result, "UTF-16LE");
    }//public String crypt (String message, byte [] codebook) throws Exception

    //Creates truly random numbers and makes a byte array using those truly random numbers
    public byte [] makeCodebook (int length) throws Exception
    {
        SecureRandom SecureRandom = new SecureRandom();//instance of SecureRandom named random
        byte[] codebook = null;

        codebook = new byte[length];
        SecureRandom.nextBytes(codebook);//generate bytes using the byte[]codebook

        //output
        System.out.println("This is the contents of the codebook: ");
        for(int i = 0; i < codebook.length;i++)
        {
            System.out.print(codebook[i]+" ");
        }//for(int i = 0; i < codebook[i];i++)
        //output
        System.out.println();
        return codebook;
    }//public byte [] MakeCodebook (int length) throws Exception

}//Public class Crypto
2

There are 2 answers

1
shareef On

you should check your code for ArrayIndexOutOfBoundsException error if statment added check below and when i try non english character like arabic it always thrown //XOR codebook and encryptedMessage System.out.println("This is the message using XOR:"); for(int i = 0; i

            //since XOR has return type of an int, we cast it to a byte
            if(result.length<=cryptedMessage.length){
            result[i] = (byte)(((byte)(codebook[i])) ^ ((byte)(cryptedMessage[i])));
            System.out.print(result[i]+" ");
            }
        }
6
QuantumMechanic On

The problem is probably because the XORing against random data occasionally produces an output that does not express a valid set of characters when interpreted as a UTF-16LE byte sequence.

Instead of trying to interpret the ciphertext as a UTF-16LE string, consider just base64-encoding the ciphertext bytes once you've produced them and returning the resulting base64-encoded string. Then when decrypting, base64-decode the input string to get back the ciphertext bytes, do the XOR to get your plaintext bytes, and then create the plaintext string from the plaintext bytes by interpreting the plaintext bytes as a UTF-16LE sequence.


Updated to reply to a comment below without having to worry about running out of space.

As I said, you could base64-encode the ciphertext bytes. That's certainly doable in Java. The commons-codec library has methods to do it and a search will find others. If you're not allowed to use outside libraries, then roll your own method of converting arbitrary bytes to bytes guaranteed to be a valid encoding of something.

For example, you could split each byte in your ciphertext into its high 4 bits and low 4bits. Thus each byte would produce a pair of values that each range from 0-15. Then you could produce a 2-byte sequence from each byte by adding the ASCII code for 'A' to those numbers. This is not very efficient, but it does the job.