2010-12-06 14 views
5

NOTA: Volviendo a esto más adelante ya que no he podido encontrar una solución funcional. Drenar las secuencias de entrada de forma manual en lugar de utilizar BufferedReaders no parece ser de ayuda ya que el método inputStream.read() bloquea permanentemente el programa. Puse la llamada gpg en un archivo por lotes y llamé al archivo por lotes de Java para obtener el mismo resultado. Una vez que se llama a gpg con la opción de descifrado, la secuencia de entrada parece ser inaccesible, bloqueando todo el programa. Tendré que volver a esto cuando tenga más tiempo para concentrarme en la tarea. Mientras tanto, tendré que hacer que el descifrado funcione por otros medios (probablemente BouncyCastle).Llamar a GnuPG en Java a través de un Runtime Process para encriptar y desencriptar archivos: el descifrado siempre cuelga

La última opción para tratar probablemente es llamar cmd.exe, y escribir el comando a través del flujo de entrada generada por ese proceso ...

aprecio la ayuda en este tema.


He estado trabajando en este problema durante un par de días y no he hecho ningún progreso, así que pensé que daría vuelta a la exeprtise aquí un poco de ayuda.

Estoy creando un programa simple que llamará a GnuPG a través de un proceso de ejecución de Java. Necesita ser capaz de cifrar y descifrar archivos. El cifrado funciona, pero tengo algunos problemas para descifrar archivos. Cada vez que intento descifrar un archivo, el proceso se bloquea. exitValue() siempre arroja su IllegalThreadStateException y el programa resuena como si aún estuviera esperando. El código para estos métodos se adjunta a continuación. El objetivo final del programa es descifrar el archivo y analizar sus contenidos en Java.

He intentado tres enfoques para hacer funcionar el método gpgDecrypt. El primer enfoque consistía en eliminar la opción contraseña-fd y escribir la frase de contraseña en gpg a través de la secuencia gpgOutput en el bloque catch, suponiendo que estaba solicitando la frase de contraseña como lo haría a través de la línea de comando. Esto no funcionó, así que puse la frase de contraseña en un archivo y agregué la opción -passphrase-fd. En este caso, el programa se repite infinitamente. Si escribo algo a través de la transmisión gpgOutput, el programa se completará. El valor de Salida impreso tendrá un valor de 2 y la variable de resultado estará en blanco.

La tercera opción es BouncyCastle, pero estoy teniendo problemas para que reconozca mi clave privada (que probablemente sea una publicación separada).

Las claves que estoy usando para cifrar y descifrar son claves RSA de 4096 bits, generadas por GnuPG. En ambos casos, usando la frase de contraseña y el archivo de frase de contraseña, intenté canalizar la salida a un archivo a través del > myFile.txt, pero no parece hacer ninguna diferencia.

Aquí están los métodos gpgEncrypt, gpgDecrypt y getStreamText. Publiqué ambos ya que el cifrado funciona, y no puedo ver diferencias evidentes entre cómo estoy ejecutando y manejo el proceso entre los métodos de encriptación y desencriptación. getStreamText solo lee los contenidos de las secuencias y devuelve una cadena.

EDITAR: Nota rápida, entorno de Windows. Si copio la salida del comando de descifrado, funcionará perfectamente a través de la consola. Entonces sé que el comando es válido.


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

He hecho un ** lote ** de llamadas por lotes/guiones desde Java. Puedo decirte una cosa: tratar de construir un Cadena que contenga caracteres espaciadores es una receta para el desastre. ** NUNCA ** NUNCA llame al método * Runtime.getRuntime(). Exec (String) *. Lo que ** DEBES hacer ** es dividir tu cadena y llamar al método * Runtime.getRuntime(). Exec (String []) *. Tenga en cuenta que no estoy diciendo que va a resolver el problema que tiene aquí: todo lo que digo es que sufrirá en un momento u otro si sigue invocando lotes/secuencias de comandos de la manera que lo hace actualmente. – SyntaxT3rr0r

+0

Gracias por la entrada. Voy a refactorizar esto. Independientemente de si soluciona el problema o no, estoy de acuerdo en que es necesario hacerlo. – framauro13

+0

¿Has encontrado alguna respuesta, también me encontré con lo mismo! Empecé a codificar viendo su ejemplo al ver el problema de [stream Gobbler] (http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4#codewrap144) que usé ese también. Pero este problema colgante se mantuvo cuando ya existía un archivo y i gpg.exe estaba esperando que el usuario tocara 'y '. Pero la parte irritante fue el mensaje de seleccionar Y/N nunca aparece en la consola (Stream Gobbler), por lo que no puedo manejarlo programáticamente. El gorrón de la secuencia muestra si gpg.exe se completa con éxito. –

Respuesta

1

¿Ha intentado ejecutar ese comando desde la línea de comandos, no desde el código Java? Puede haber un problema con la opción 'solo para tus ojos', cuando GnuPG esperará la salida de la consola.

+0

El comando generado por la aplicación funciona bien cuando lo ejecuto a través de la línea de comando. Puedo intentar envolverlo en un archivo por lotes y llamar al archivo por lotes en su lugar. – framauro13

1

Esto puede o no ser el problema (de la función de descifrado)

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

usted está envolviendo el resultado de getInputStream dos veces. Obviamente, gpgErrorOutput debería estar envolviendo la secuencia de error, no la secuencia de entrada.

+1

También has leído http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html? Un uso confiable de Runtime.exec necesita hilos que lean la salida del programa. –

+0

Gracias, lo extrañé totalmente. Es curioso cómo puedes pasar horas y horas leyendo sobre las API, diseñando procesos y preocupándote por la eficiencia y cometiendo un error tonto como este :) – framauro13

+0

Actualicé el código para envolver el flujo correcto ... desafortunadamente, el bloqueo persiste. – framauro13

0
int exitValue = gpgProcess.exitValue(); 

// da proceso no ha salido excepción

0

Se trabajó para mí cuando puedo reemplazar el comando descifrar con comando a continuación

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

olvido lo maneja de Java, no hay 100 métodos para eso. Pero me he quedado atrapado consigo mismo comando de descifrado, que era muy útil, aunque usted no necesita todas esas comillas y si desea descifrar un archivo de gran tamaño, que es la siguiente:

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

me encontré con este hilo hoy porque estaba teniendo exactamente el mismo problema en cuanto a la ejecución del programa. El hilo de Cameron desde arriba contiene la solución, que es que debes drenar el inputStream de tu proceso. Si no lo haces, la transmisión se llena y se cuelga. Simplemente agregando

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

Antes de verificar la salida, el valor se lo corrigió por mí.

Cuestiones relacionadas