Android Dev Android Dev - 5 months ago 8
Linux Question

JAR file works as expected on Linux, throws exceptions on Windows

I have written a basic text encryption program in Java. (Yes, I'm aware that it uses ECB...)

My program works fine on Linux. I complied it as a

JAR
file, and that also works fine on Linux. The problem is that when I run the file on Windows, it will throw an exception while decrypting the same text with the same key that worked on Ubuntu.

I have no idea where to start debugging or even what search terms to use on Google. I am at a complete loss. I thought Java was cross-platform.

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

public class Application
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
String textToEncrypt = "Hello World";
String textToDecrypt;
String textToDecryptAscii;
String result;
int operation;
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
} catch (NoSuchAlgorithmException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (NoSuchPaddingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//String key = "Bar12345Bar12345"; // 128 bit key
String key = null;

BASE64Encoder asciiEncoder = new BASE64Encoder();
BASE64Decoder asciiDecoder = new BASE64Decoder();

System.out.printf("Enter:\n1 for encryption\n2 for decryption\n\nChoice: ");
operation = input.nextInt();
input.nextLine();

if (operation == 1)
{
try
{
System.out.print("Enter a 128-bit key to be used for encryption: ");
key = input.nextLine();

if(key.length() != 16)
{
while(key.length() != 16)
{
System.out.print("You need to enter a *128-bit* key: ");
key = input.nextLine();
}
}

System.out.printf("\n---------\n\nText to encrypt: ");
textToEncrypt = input.nextLine();

//Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
//Cipher cipher = Cipher.getInstance("AES");

//encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(textToEncrypt.getBytes());

StringBuilder sb = new StringBuilder();
for (byte b: encrypted)
{
sb.append((char)b);
}

// the encrypted String
String enc = sb.toString();
//System.out.println("encrypted:" + enc);

String asciiEncodedEncryptedResult = asciiEncoder.encodeBuffer(enc.getBytes());

asciiEncodedEncryptedResult = asciiEncodedEncryptedResult.replace("\n", "").replace("\r", "");

System.out.println("Encrypted text: " + asciiEncodedEncryptedResult);
//System.out.printf("\n------------------------------\nDecrypted text: " + asciiEncodedEncryptedResult + "\n------------------------------\n\n\n");

}
catch(Exception e)
{
e.printStackTrace();
}
}
else if (operation == 2)
{
System.out.printf("\n---------\n\nText to decrypt: ");
textToDecryptAscii = input.nextLine();

System.out.print("Enter the 128-bit decryption key: ");
key = input.nextLine();

if(key.length() != 16)
{
while(key.length() != 16)
{
System.out.print("You need to enter a *128-bit* key: ");
key = input.nextLine();
}
}

byte[] decodedBytes = null;
try
{
decodedBytes = asciiDecoder.decodeBuffer(textToDecryptAscii);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//System.out.println("decodedBytes " + new String(decodedBytes));

textToDecrypt = new String(decodedBytes);

//Convert the string to byte array
//for decryption
byte[] bb = new byte[textToDecrypt.length()];
for (int i=0; i<textToDecrypt.length(); i++)
{
bb[i] = (byte) textToDecrypt.charAt(i);
}

//decrypt the text
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
try
{
cipher.init(Cipher.DECRYPT_MODE, aesKey);
}
catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String decrypted = null;
try
{
decrypted = new String(cipher.doFinal(bb));
}
catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.printf("\n------------------------------\nDecrypted text: " + decrypted + "\n------------------------------\n\n\n");
}
}
}

Answer

I've seen this before doing encryption because of the differences in CRLF between Linux/Unix and Windows. The two operating systems see Carriage Return Line Feed and Line Feed quite differently.

You may need to compile your code into a jar file using Ant and the fixcrlf step :

    <fixcrlf 
        srcdir="${dir.prj.work}"
        eol="dos"
        includes="**/*"
    />

Also, it may not be your code...If the text your are feeding in doesn't have the correct CRLF encoded, your encryption will fail as well.

Edit :

For what it's worth, below is an abridged version of your code, that skips operations...it just takes in the key, does the encryption then turns around and decrypts all in one stream...I got tired of inputting the same operations, same keys, etc...

Anyway, the following code works without the BASE64 Encoder, Decoder...I couldn't get it to work with the BASE64. Although output from tests showed the BASE64 "seemed" to be working as I was getting what looked like the correct encrypted text after the Decoder, but it still continued to throw the error.

However, if you take the BASE64 Encoder and Decoder out of the picture, the encryption/decryption works fine...Also, I added a few key .trim() in there as I saw invisible CRLF being introduced in the String components.

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

public class Application { 

    public Application() {
        // TODO Auto-generated constructor stub
    }

    public static void main ( String[] args ) {
        Scanner input = new Scanner(System.in);
        String textToEncrypt = "Hello World";
        String textToDecrypt;
        String textToDecryptAscii;
        String result;
        int operation;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (NoSuchPaddingException e1) {
            e1.printStackTrace();
        }

        //String key = "Bar12345Bar12345"; // 128 bit key
        String key = null;
        //byte[] key = null;

        //BASE64Encoder asciiEncoder = new BASE64Encoder();
        //BASE64Decoder asciiDecoder = new BASE64Decoder();

        //System.out.printf("Enter:\n1 for encryption\n2 for decryption\n\nChoice: ");
        //operation = input.nextInt();
        //input.nextLine();

        try { 
            System.out.print("Enter a 128-bit key to be used for encryption: ");
            key = input.nextLine();

            if ( key.length() != 16 ) {
                while ( key.length() != 16 ) {
                    System.out.print("You need to enter a *128-bit* key: ");
                    key = input.nextLine();
                }
            }
            System.out.println ( "128-bit encryption key.......................["+key+"] length ["+key.length ()+"]");

            System.out.printf ( "Text to encrypt..............................[");
            //System.out.printf("\n---------\n\nText to encrypt: ");
            textToEncrypt = input.nextLine();
            System.out.println ( "Text to encrypt..............................["+textToEncrypt+"] length ["+textToEncrypt.length ()+"]");

            //Create key and cipher
            Key aesKey = new SecretKeySpec(key.trim().getBytes(), "AES");
            //Cipher cipher = Cipher.getInstance("AES");

            //encrypt the text
            cipher.init(Cipher.ENCRYPT_MODE, aesKey);
            byte[] encrypted = cipher.doFinal(textToEncrypt.getBytes ());

            StringBuilder sb = new StringBuilder();
            for (byte b: encrypted) {
                sb.append((char)b);
            }

            // the encrypted String
            String enc = sb.toString();
            System.out.println ( "Encrypted text...............................["+enc+"] length ["+enc.length ()+"]");
            //System.out.println("encrypted:" + enc);

            //String asciiEncodedEncryptedResult = asciiEncoder.encodeBuffer(enc.getBytes()).trim ();
            String asciiEncodedEncryptedResult = enc.trim ();
            System.out.println ( "Encoded text.................................["+asciiEncodedEncryptedResult+"] length ["+asciiEncodedEncryptedResult.length ()+"]");

            //asciiEncodedEncryptedResult = asciiEncodedEncryptedResult.replace("\n", "").replace("\r", "");
            asciiEncodedEncryptedResult = asciiEncodedEncryptedResult.trim ();

            System.out.println ( "Encrypted text...............................["+asciiEncodedEncryptedResult+"] length ["+asciiEncodedEncryptedResult.length ()+"]");


            //byte[] decodedBytes = null;
            //try {
            //    decodedBytes = asciiDecoder.decodeBuffer(asciiEncodedEncryptedResult);
            //} 
            //catch (IOException e1) {
            //    e1.printStackTrace();
            //}
            //System.out.println ( "Decoded Bytes................................["+decodedBytes+"] length ["+decodedBytes.length+"]");

            //textToDecrypt = new String(decodedBytes);
            textToDecrypt = asciiEncodedEncryptedResult;

            System.out.println ( "Text to Decrypt..............................["+textToDecrypt+"] length ["+textToDecrypt.length()+"]");

            //Convert the string to byte array
            //for decryption
            byte[] bb = new byte[textToDecrypt.length()];
            for ( int i=0; i<textToDecrypt.length(); i++ ) {
                bb[i] = (byte) textToDecrypt.charAt(i);
            }

            //decrypt the text
            //Key aesKey = null;
            String decrypted = null;
            try {
                //aesKey = new SecretKeySpec(key.trim ().getBytes (), "AES");
                cipher.init(Cipher.DECRYPT_MODE, aesKey);
                decrypted = new String(cipher.doFinal(bb));
            }
            catch (InvalidKeyException e) {
                e.printStackTrace();
            }
            catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }
            catch (BadPaddingException e) {
                e.printStackTrace();
            }
            catch ( Exception ltheXcp ) { 
                ltheXcp.printStackTrace ();
            }

            if ( decrypted != null ) {
                System.out.println ( "Decrypted text...............................["+decrypted+"] length ["+decrypted.length ()+"]");
            }
            else { 
                System.out.println ( "Decrypted text...............................["+decrypted+"] length []");
            }

        }
        catch ( Exception ltheXcp ) { 
            ltheXcp.printStackTrace ();
        }

    }
}

Sample output :

Enter a 128-bit key to be used for encryption: aaaaaaaaaaaaaaaa
128-bit encryption key.......................[aaaaaaaaaaaaaaaa] length [16]
Text to encrypt..............................[adymlincoln
Text to encrypt..............................[adymlincoln] length [11]
Encrypted text...............................[\_i8`???R????] length [16]
Encoded text.................................[\_i8`???R????] length [16]
Encrypted text...............................[\_i8`???R????] length [16]
Text to Decrypt..............................[\_i8`???R????] length [16]
Decrypted text...............................[adymlincoln] length [11]

Bottom line, there is "something" in the BASE64 Encoder/Decoder that is causing the decryption to fail...It's changing the bitwise characters in some fashion that I couldn't see using System.out.println()...perhaps a Hexidecimal output might show what...