通过运行时进程在Java中调用GnuPG来加密和解密文件 - Decrypt始终挂起

时间:2010-12-06 13:57:59

标签: java encryption runtime.exec gnupg

注意:稍后再回来,因为我无法找到有效的解决方案。手动排出输入流而不是使用BufferedReaders似乎没有帮助,因为inputStream.read()方法永久阻止程序。我将gpg调用放在批处理文件中,并从Java调用批处理文件以获得相同的结果。一旦使用decrypt选项调用gpg,输入流似乎变得不可访问,阻塞整个程序。当我有更多时间专注于这项任务时,我将不得不回到这一点。与此同时,我将不得不通过其他方式(可能是BouncyCastle)来解密。

可能尝试的最后一个选项是调用cmd.exe,并通过该进程生成的输入流编写命令...

我很感谢在这个问题上的帮助。


我已经在这个问题上工作了几天而没有取得任何进展,所以我想我会在这里寻求帮助。

我正在创建一个简单的程序,它将通过Java运行时进程调用GnuPG。它需要能够加密和解密文件。加密有效,但我在解密文件时遇到了一些问题。每当我尝试解密一个文件时,进程都会挂起。exitValue()总是抛出它的IllegalThreadStateException,程序会突然停止,就像它还在等待一样。这些方法的代码如下。该程序的最终目标是解密文件,并用Java解析它的内容。

我尝试了三种方法来使gpgDecrypt方法起作用。第一种方法涉及删除passphrase-fd选项并通过catch块中的gpgOutput流将密码短语写入gpg,假设它正在通过命令行提示输入密码。这不起作用,所以我将密码放在一个文件中并添加了-passphrase-fd选项。在这种情况下,程序无限重复。如果我通过gpgOutput流写任何东西,程序将完成。打印的Exit值的值为2,结果变量为空。

第三个选项是BouncyCastle,但是我在识别我的私钥时遇到了问题(这可能是一个单独的帖子)。

我用来加密和解密的密钥是由GnuPG生成的4096位RSA密钥。在使用密码短语和密码短语文件的两种情况下,我都尝试通过> myFile.txt将输出汇总到一个文件,但它似乎没有任何区别。

以下是gpgEncrypt,gpgDecrypt和getStreamText方法。我发布了两个,因为加密工作,我看不出我如何执行和处理加密和解密方法之间的过程之间的任何明显差异。 getStreamText只读取流的内容并返回一个字符串。

编辑:快速说明,Windows环境。如果我复制decrypt命令输出,它可以通过控制台正常工作。所以我知道命令是有效的。


public boolean gpgEncrypt(String file, String recipient, String outputFile){
    boolean success = true;
    StringBuilder gpgCommand = new StringBuilder("gpg --recipient \"");
    gpgCommand.append(recipient).append("\" --output \"").append(outputFile).append("\" --yes --encrypt \"");
    gpgCommand.append(file).append("\"");

    System.out.println("ENCRYPT COMMAND: " + gpgCommand);
    try {
        Process gpgProcess = Runtime.getRuntime().exec(gpgCommand.toString());

        BufferedReader gpgOutput = new BufferedReader(new InputStreamReader(gpgProcess.getInputStream()));
        BufferedWriter gpgInput = new BufferedWriter(new OutputStreamWriter(gpgProcess.getOutputStream()));
        BufferedReader gpgErrorOutput = new BufferedReader(new InputStreamReader(gpgProcess.getErrorStream()));

        boolean executing = true;

        while(executing){
            try{
                int exitValue = gpgProcess.exitValue();

                if(gpgErrorOutput.ready()){
                    String error = getStreamText(gpgErrorOutput);
                    System.err.println(error);
                    success = false;
                    break;
                }else if(gpgOutput.ready()){
                    System.out.println(getStreamText(gpgOutput));
                }

                executing = false;
            }catch(Exception e){
                //The process is not yet ready to exit.  Take a break and try again.
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    System.err.println("This thread has insomnia: " + e1.getMessage());
                }
            }
        }
    } catch (IOException e) {
        System.err.println("Error running GPG via runtime: " + e.getMessage());
        success = false;
    }

    return success;
}

public String gpgDecrypt(String file, String passphraseFile){
    String result = null;
    StringBuilder command = new StringBuilder("gpg --passphrase-fd 0 --decrypt \"");
    command.append(file).append("\" 0<\"").append(passphraseFile).append("\"");             
    System.out.println("DECRYPT COMMAND: " + command.toString());
    try {

        Process gpgProcess = Runtime.getRuntime().exec(command.toString());

        BufferedReader gpgOutput = new BufferedReader(new InputStreamReader(gpgProcess.getInputStream()));
        BufferedReader gpgErrorOutput = new BufferedReader(new InputStreamReader(gpgProcess.getErrorStream()));
        BufferedWriter gpgInput = new BufferedWriter(new OutputStreamWriter(gpgProcess.getOutputStream()));

        boolean executing = true;

        while(executing){
            try{
                if(gpgErrorOutput.ready()){
                    result = getStreamText(gpgErrorOutput);
                    System.err.println(result);
                    break;
                }else if(gpgOutput.ready()){
                    result = getStreamText(gpgOutput);
                }

                int exitValue = gpgProcess.exitValue();
                System.out.println("EXIT: " + exitValue);

                executing = false;
            }catch(IllegalThreadStateException e){
                System.out.println("Not yet ready.  Stream status: " + gpgOutput.ready() + ", error: " + gpgErrorOutput.ready());

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    System.err.println("This thread has insomnia: " + e1.getMessage());
                }
            }
        }
    } catch (IOException e) {
        System.err.println("Unable to execute GPG decrypt command via command line: " + e.getMessage());
    }

    return result;
}

private String getStreamText(BufferedReader reader) throws IOException{
    StringBuilder result = new StringBuilder();
    try{
        while(reader.ready()){
            result.append(reader.readLine());
            if(reader.ready()){
                result.append("\n");
            }
        }
    }catch(IOException ioe){
        System.err.println("Error while reading the stream: " + ioe.getMessage());
        throw ioe;
    }
    return result.toString();
}

7 个答案:

答案 0 :(得分:3)

我忘记了你在Java中如何处理它,有100种方法。但是我一直坚持使用decrypt命令,它非常有用,虽然你不需要所有这些引用,如果你想解密一个大文件,它就是这样的:

gpg --passphrase-fd 0 --output yourfile.txt --decrypt /encryptedfile.txt.gpg/ 0</passwrdfile.txt

答案 1 :(得分:1)

您是否尝试过从命令行运行该命令,而不是从Java代码运行该命令? 当GnuPG等待控制台输出时,可能存在“仅供您选择”选项的问题。

答案 2 :(得分:1)

这可能是问题,也可能不是(在解密函数中)

    BufferedReader gpgOutput = new BufferedReader(new InputStreamReader(gpgProcess.getInputStream()));
    BufferedReader gpgErrorOutput = new BufferedReader(new InputStreamReader(gpgProcess.getInputStream()));
    BufferedWriter gpgInput = new BufferedWriter(new OutputStreamWriter(gpgProcess.getOutputStream()));

您将getInputStream的结果包装两次。显然gpgErrorOutput应该包装错误流,而不是输入流。

答案 3 :(得分:1)

我今天偶然发现了这个帖子,因为就程序挂起我遇到了完全相同的问题。 Cameron的上面的线程包含解决方案,即您必须从进程中耗尽inputStream。如果不这样做,则流会填满并挂起。只需添加

String line = null;
while ( (line = gpgOutput.readLine()) != null ) {
     System.out.println(line);
}

在检查exitValue之前,请为我修复它。

答案 4 :(得分:0)

int exitValue = gpgProcess.exitValue();

//它让进程没有退出异常

答案 5 :(得分:0)

当我用下面的命令

替换decrypt命令时,它对我有用
gpg --output decrypted_file --batch --passphrase "passphrase goes here" --decrypt encrypted_file

答案 6 :(得分:0)

以下代码可用于GNUPG 2.1.X

public static boolean gpgEncrypt(String file, String recipient,
        String outputFile) {
    boolean success = true;
    StringBuilder gpgCommand = new StringBuilder("gpg --recipient \"");
    gpgCommand.append(recipient).append("\" --output \"")
            .append(outputFile).append("\" --yes --encrypt \"");
    gpgCommand.append(file).append("\"");

    System.out.println("ENCRYPT COMMAND: " + gpgCommand);
    try {
        Process gpgProcess = Runtime.getRuntime().exec(
                gpgCommand.toString());

        BufferedReader gpgOutput = new BufferedReader(
                new InputStreamReader(gpgProcess.getInputStream()));
        BufferedWriter gpgInput = new BufferedWriter(
                new OutputStreamWriter(gpgProcess.getOutputStream()));
        BufferedReader gpgErrorOutput = new BufferedReader(
                new InputStreamReader(gpgProcess.getErrorStream()));

        boolean executing = true;

        while (executing) {
            try {
                int exitValue = gpgProcess.exitValue();

                if (gpgErrorOutput.ready()) {
                    String error = getStreamText(gpgErrorOutput);
                    System.err.println(error);
                    success = false;
                    break;
                } else if (gpgOutput.ready()) {
                    System.out.println(getStreamText(gpgOutput));
                }

                executing = false;
            } catch (Exception e) {
                // The process is not yet ready to exit. Take a break and
                // try again.
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    System.err.println("This thread has insomnia: "
                            + e1.getMessage());
                }
            }
        }
    } catch (IOException e) {
        System.err.println("Error running GPG via runtime: "
                + e.getMessage());
        success = false;
    }

    return success;
}

// gpg --pinentry-mode=loopback --passphrase "siv_test" -d -o
// "sample_enc_data_op.txt" "sample_enc_data_input.gpg"

public static String gpgDecrypt(String file, String passphrase,
        String outputfile) {
    String result = null;
    StringBuilder command = new StringBuilder(
            "gpg --pinentry-mode=loopback --passphrase \"");
    command.append(passphrase).append("\" -d -o \"").append(outputfile)

    .append("\" --yes \"").append(file)

    .append("\"");
    System.out.println("DECRYPT COMMAND: " + command.toString());
    try {

        Process gpgProcess = Runtime.getRuntime().exec(command.toString());

        BufferedReader gpgOutput = new BufferedReader(
                new InputStreamReader(gpgProcess.getInputStream()));
        BufferedReader gpgErrorOutput = new BufferedReader(
                new InputStreamReader(gpgProcess.getErrorStream()));
        BufferedWriter gpgInput = new BufferedWriter(
                new OutputStreamWriter(gpgProcess.getOutputStream()));

        boolean executing = true;

        while (executing) {
            try {
                if (gpgErrorOutput.ready()) {
                    result = getStreamText(gpgErrorOutput);
                    System.err.println(result);
                    break;
                } else if (gpgOutput.ready()) {
                    result = getStreamText(gpgOutput);
                }
                String line = null;
                while ((line = gpgOutput.readLine()) != null) {
                    System.out.println(line);
                }
                int exitValue = gpgProcess.exitValue();
                System.out.println("EXIT: " + exitValue);

                executing = false;
            } catch (IllegalThreadStateException e) {
                System.out.println("Not yet ready.  Stream status: "
                        + gpgOutput.ready() + ", error: "
                        + gpgErrorOutput.ready());

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    System.err.println("This thread has insomnia: "
                            + e1.getMessage());
                }
            }
        }
    } catch (IOException e) {
        System.err
                .println("Unable to execute GPG decrypt command via command line: "
                        + e.getMessage());
    }

    return result;
}

private static String getStreamText(BufferedReader reader)
        throws IOException {
    StringBuilder result = new StringBuilder();
    try {
        while (reader.ready()) {
            result.append(reader.readLine());
            if (reader.ready()) {
                result.append("\n");
            }
        }
    } catch (IOException ioe) {
        System.err.println("Error while reading the stream: "
                + ioe.getMessage());
        throw ioe;
    }
    return result.toString();
}