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

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

问题描述:

注意:稍后回顾,因为我找不到工作解决方案。因为inputStream.read()方法永久阻止程序,所以手动排空输入流而不是使用BufferedReaders似乎没有帮助。我将gpg调用放在一个批处理文件中,并从Java调用该批处理文件以获得相同的结果。一旦使用解密选项调用gpg,输入流似乎变得无法访问,从而阻止了整个程序。当我有更多时间专注于这项任务时,我必须回到这个问题。同时,我必须通过其他方式解密工作(可能是BouncyCastle)。在Java中通过运行时进程调用GnuPG以加密和解密文件 - 解密始终挂起

最后一个选项可能会尝试是调用CMD.EXE,并通过这一过程产生的输入流写入命令

我明白在这个问题上的援助。


我一直在这个问题上几天,并没有取得任何进展,所以我想我会转向exeprtise这里一些帮助。

我创建一个简单的程序,将通过Java运行过程调用的GnuPG。它需要能够加密和解密文件。加密工作,但我有一些解密文件的问题。每当我尝试解密文件时,该过程都会挂起。 exitValue()总是抛出它的IllegalThreadStateException,并且程序突然发生,好像它仍在等待。这些方法的代码附在下面。该程序的最终目标是解密文件,并用Java解析它的内容。

我已经尝试了三种方法来获得gpgDecrypt方法工作。第一种方法是通过删除passphrase-fd选项并通过catch块中的gpgOutput流将密码写入gpg,假设它通过命令行提示输入密码。这不起作用,所以我把密码放在一个文件中并添加了-passphrase-fd选项。在这种情况下,程序无限重复。如果我通过gpgOutput流写入任何内容,程序将完成。打印的出口值将为2,结果变量将为空白。

第三个选项是BouncyCastle的,但我有得到它的问题认识我的私有密钥(这可能是一个独立的后一起)。

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

这里有gpgEncrypt,gpgDecrypt和getStreamText方法。自从加密工作后,我都发布了两篇文章,而且在执行和处理加密和解密方法之间的过程之间我看不到任何明显的差异。 getStreamText只是读取流的内容并返回一个字符串。

编辑:快速笔记,Windows环境。如果我复制解密命令输出,它通过控制台工作得很好。所以我知道这个命令是有效的。


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

我已经完成了从Java批量/脚本调用**很多**。我可以告诉你一件事:试图构建一个包含间距字符的字符串是灾难的秘诀。做**永远不要**调用* Runtime.getRuntime()。exec(String)*方法。你**应该做的是分割你的String并调用* Runtime.getRuntime()。exec(String [])*方法。请注意,我并不是说它会解决您在此处遇到的问题:我所说的是,如果您按照当前的方式继续调用批处理/脚本,您将会受到某种程度的影响。 – SyntaxT3rr0r 2010-12-06 14:20:51

+0

感谢您的意见。我会重构这个。不管它是否解决了这个问题,我同意它确实需要完成。 – framauro13 2010-12-06 15:19:06

+0

你有没有找到答案,我也遇到过同样的事情!我开始编码,看到你的例子看到[溪流狼吞虎咽](http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4#codewrap144)问题,我用那个太。但是,当文件已经存在并且我gpg.exe正在等待用户点击'y '时,这个悬挂问题依然存在。但令人不快的部分是提示选择Y/N永远不会出现在控制台(Stream Gobbler)中,所以我无法通过编程来处理。流gobbler确实显示gpg.exe是否成功完成。 – 2011-11-25 18:27:40

你试过从命令行运行该命令,而不是从Java代码? 当GnuPG等待控制台输出时,可能会出现'仅限于您的眼睛'选项的问题。

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

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应该包装错误流,而不是输入流。

int exitValue = gpgProcess.exitValue(); 

//它给进程还没有退出异常

它的工作对我来说,当我更换以下命令

gpg --output decrypted_file --batch --passphrase "passphrase goes here" --decrypt encrypted_file 

解密命令我忘了你是如何处理它在Java中,有是100个方法。但我坚持了解密命令本身,这是非常有帮助的,虽然你并不需要所有的报价,如果你想解密大文件,它是这样的:

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

我偶然发现了这个线程今天是因为我在程序挂起时遇到了完全相同的问题。 Cameron上面的线程包含解决方案,这就是您必须从流程中消耗inputStream。如果你不这样做,那么这个小河就会填满并挂起。只需加入

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

在检查exitValue之前,确定了它。