【问题标题】:JAR file works as expected on Linux, throws exceptions on WindowsJAR 文件在 Linux 上按预期工作,在 Windows 上引发异常
【发布时间】:2016-08-08 20:43:12
【问题描述】:

我用 Java 编写了一个基本的文本加密程序。 (是的,我知道它使用欧洲央行......)

我的程序在 Linux 上运行良好。我将它编译为JAR 文件,这在 Linux 上也可以正常工作。问题是,当我在 Windows 上运行文件时,它会抛出异常,同时使用 same 密钥解密 same text 在 Ubuntu 上运行。

我不知道从哪里开始调试,甚至不知道在 Google 上使用什么搜索词。我完全不知所措。 我认为 Java 是跨平台的。

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

【问题讨论】:

  • 你得到什么异常?一个可能的问题是将 String 转换为 byte[]。 String.getBytes()会在系统属性中使用file.encoding,这个值在不同的环境中可能会有所不同。
  • @beckyang - javax.crypto.BadPaddingException: Given final block not properly padded
  • 不要将原始字节附加到字符串。这会给您带来各种编码问题。使用= asciiEncoder.encodeBuffer(encrypted); 进行解密使用decodedBytes 而不是字符串和密钥,请确保在base64 中输出/输入它(或使用密码派生)。当您的密钥是 ascii 时,它很弱,但不应该是互操作的问题。

标签: java linux windows exception jar


【解决方案1】:

由于 Linux/Unix 和 Windows 之间 CRLF 的差异,我在进行加密之前已经看到了这一点。这两种操作系统对回车换行和换行的看法截然不同。

您可能需要使用 Ant 和 fixcrlf 步骤将代码编译成 jar 文件:

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

另外,它可能不是您的代码...如果您输入的文本没有正确的 CRLF 编码,您的加密也会失败。

编辑:

对于它的价值,下面是您的代码的精简版本,它跳过了操作......它只接受密钥,然后加密然后转身并在一个流中解密......我厌倦了输入相同的操作,相同的键,等等……

无论如何,以下代码在没有 BASE64 编码器、解码器的情况下也可以工作……我无法让它与 BASE64 一起工作。尽管测试的输出显示 BASE64“似乎”在工作,因为我在解码器之后得到了看起来正确的加密文本,但它仍然继续抛出错误。

但是,如果您将 BASE64 编码器和解码器从图片中取出,则加密/解密工作正常...此外,我在其中添加了一些密钥 .trim(),因为我看到在字符串中引入了不可见的 CRLF组件。

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

    }
}

样本输出:

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]

底线,BASE64 编码器/解码器中有“某些东西”导致解密失败......它正在以某种方式更改按位字符,而我使用 System.out.println() 看不到。 ..也许十六进制输出可能会显示什么...

【讨论】:

  • 我还应该提到,在 Windows 上加密的文本不会在 Windows 上解密。什么鬼?
  • 感谢您抽出宝贵时间帮助我。如果 BASE64 编码器导致解密失败,是否有另一种方法可以将加密字符串转换为仅使用标准 ASCII 字符的字符串? 您无法在电子邮件中准确发送非 ASCII 文本。 ..
  • 代码执行相同的(错误的)字节/字符串转换。它们都可以删除,例如:gist.github.com/anonymous/7017dc776d502e0476b1dd6e4670a16c
猜你喜欢
  • 2023-02-12
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多