Вызов GnuPG в Java через Runtime Process для шифрования и дешифрования файлов - Decrypt всегда зависает - PullRequest
5 голосов
/ 06 декабря 2010

ПРИМЕЧАНИЕ: Возвращаясь к этому позже, так как я не смог найти работающего решения.Удаление потоков ввода вручную вместо использования BufferedReaders, похоже, не помогает, так как метод inputStream.read () навсегда блокирует программу.Я поместил вызов gpg в пакетный файл и вызвал пакетный файл с Java, чтобы получить только тот же результат.Когда gpg вызывается с опцией decrypt, входной поток становится недоступным, блокируя всю программу.Я должен вернуться к этому, когда у меня будет больше времени, чтобы сосредоточиться на задаче.В то же время, мне нужно, чтобы расшифровка работала другими способами (вероятно, BouncyCastle).

Последний вариант, который, вероятно, попытается сделать, это вызвать cmd.exe и написать команду через сгенерированный входной потокс помощью этого процесса ...

Я ценю помощь в этом вопросе.


Я работал над этой проблемой в течение нескольких дней и не добился никакого прогресса, поэтомуЯ подумал, что я обращусь к этой статье за ​​помощью.

Я создаю простую программу, которая будет вызывать GnuPG через процесс выполнения Java.Он должен быть в состоянии зашифровать и расшифровать файлы.Шифрование работает, но у меня возникают проблемы с расшифровкой файлов.Всякий раз, когда я пытаюсь расшифровать файл, процесс зависает. exitValue() всегда выбрасывает исключение IllegalThreadStateException, и программа запускает его, как будто он все еще ждет.Код для этих методов прилагается ниже.Конечная цель программы - расшифровать файл и проанализировать его содержимое в Java.

Я пробовал три подхода к тому, чтобы заставить метод gpgDecrypt работать.Первый подход включал удаление опции passphrase-fd и запись парольной фразы в gpg через поток gpgOutput в блоке catch, предполагая, что он запрашивает парольную фразу, как это было бы через командную строку.Это не сработало, поэтому я поместил фразу-пароль в файл и добавил опцию -passphrase-fd.В этом случае программа повторяется бесконечно.Если я что-то напишу через поток gpgOutput, программа завершится.Напечатанное значение Exit будет иметь значение 2, а переменная результата будет пустой.

Третий вариант - BouncyCastle, но у меня возникают проблемы с его распознаванием моего личного ключа (который, вероятно, является отдельнымпубликовать все вместе).

Ключи, которые я использую для шифрования и дешифрования, - это 4096-битные ключи RSA, сгенерированные GnuPG.В обоих случаях, используя парольную фразу и файл парольной фразы, я попытался передать вывод в файл с помощью > 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();
}

Ответы [ 6 ]

3 голосов
/ 27 октября 2011

Я забыл, как вы справляетесь с этим в Java, для этого есть 100 методов.Но я застрял с самой командой расшифровки, это было очень полезно, хотя вам не нужны все эти кавычки, и если вы хотите расшифровать большой файл, это выглядит так:

gpg --passphrase-fd 0 --output yourfile.txt --decrypt /encryptedfile.txt.gpg/ 0</passwrdfile.txt
1 голос
/ 21 февраля 2013

Я наткнулся на этот поток сегодня, потому что у меня была точно такая же проблема, что и зависание программы.Поток Кэмерона сверху содержит решение, которое заключается в том, что вы должны сливать inputStream из вашего процесса.Если вы этого не сделаете, поток заполняется и зависает.Просто добавив

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

Перед проверкой exitValue исправил это для меня.

1 голос
/ 06 декабря 2010

Это может быть или не быть проблемой (в функции дешифрования)

    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 должен переносить поток ошибок, а не входной поток.

1 голос
/ 06 декабря 2010

Вы пытались запустить эту команду из командной строки, а не из кода Java? Может быть проблема с опцией «только для ваших глаз», когда GnuPG будет ожидать вывода на консоль.

0 голосов
/ 29 декабря 2010

Это сработало для меня, когда я заменил команду расшифровки на следующую команду

gpg --output decrypted_file --batch --passphrase "passphrase goes here" --decrypt encrypted_file
0 голосов
/ 28 декабря 2010
int exitValue = gpgProcess.exitValue();

// выдаёт исключение из процесса

...