Home > Software design >  Run multiple unix commands from a java program
Run multiple unix commands from a java program

Time:12-31

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
  public static void main(String[] args) throws IOException, InterruptedException {
    // Set the key and iv values
    String key = "1234567812345678";
    String iv = "1234567812345678";

    // Set the string to encrypt
    String string = "passwordToBeEncrypted";

    // Create the ProcessBuilder object and specify the command and its arguments
    ProcessBuilder pb = new ProcessBuilder("echo", string, "|", "openssl enc -aes-256-cbc -k", key, "-iv", iv, "-base64");

    // Start the process and get the Process object
    Process p = pb.start();

    // Wait for the process to finish and get the exit code
    int exitCode = p.waitFor();
    System.out.println("Exit code: "   exitCode);

    // Read the output from the process
    BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String encryptedString = reader.readLine();

    // Print the encrypted string
    System.out.println("Encrypted string: "   encryptedString);
  }
}

I am expecting the output to be a encrypted string whereas the actual output is command itself ie its printing the command "Encrypted string: passwordToBeEncrypted | openssl enc -aes-256-cbc -k 1234567812345678 -iv 1234567812345678 -base64 " Please point the issue with the above code.

I am trying to execute the below shell commands from java code -

key="1234567812345678" iv="1234567812345678"

string="passwordToBeEncrypted "

encrypted_string=$(echo "$string" | openssl enc -aes-256-cbc -k "$key" -iv "$iv" -base64)

echo "$encrypted_string"

CodePudding user response:

As our deceased chief executive commented, Java ProcessBuilder (and also Runtime.exec) is not a shell -- and it does not run Unix 'commands' but rather programs. Many of the commands you give to a Unix shell actually run a program, but they can do other things as well such as piping or otherwise redirecting I/O, and substituting variables as in the commands you posted, and ProcessBuilder does not do these other things with the exception of piping in Java 9 up. See the compilation in my answer at Shell command in Java with Runtime.getRuntime().exec(); .

However, you don't actually need a pipeline; ProcessBuilder by default redirects the stdin and stdout (and also stderr) of the program it runs to pipes respectively from and to the Java process, which your code can use. In particular while the Unix shell commandline

echo string | openssl enc ...

runs the echo program to output 'string' (plus a newline) which is redirected to a pipe that is then redirected as the input of the openssl program, in some shells you can get exactly the same result from

openssl <<<string enc ...

which runs one program, openssl, with its input redirected from a tempfile or pipe created by the shell itself (not a separate program) and ProcessBuilder can effectively do this latter:

    String key = "1234567812345678";
    String iv = "1234567812345678";
    String string = "passwordToBeEncrypted";

    ProcessBuilder pb = new ProcessBuilder("openssl","enc","-aes-256-cbc","-k",key,"-iv",iv,"-base64");
    // note every token parsed from the shell command as a separate argument (which is every one
    // separated by unquoted whitespace) must be a separate argument to ProcessBuilder
    Process p = pb.start();
    // **CHANGE HERE**
    p.getOutputStream.write( (string "\n").getBytes(/*charset if needed*/) );
    p.getOutputStream.close();
    // instead of using the OutputStream directly you can wrap (a) Writer(s) on it and use that(them),
    // similar to the way you wrap Readers on the InputStream below, but I didn't bother

    // if the data contains any non-ASCII character(s) then the 'charset' used for encoding matters; 
    // you need to specify it in `String.getBytes()`, or when creating Writer(s) if you do that,
    // as a Java value equivalent to the 'locale' used by the echo program in your shell, 
    // which will generally be the setting of the env var LC_ALL or LANG -- 
    // but your posted data is all ASCII so all charsets Java can default to are okay

    int exitCode = p.waitFor();
    System.out.println("Exit code: "   exitCode);
    BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String encryptedString = reader.readLine();

    System.out.println("Encrypted string: "   encryptedString);

PS: all of the attempts/versions you posted encrypt -- not decrypt -- a string, in a particular way idiosyncratic to openssl that is not fully standard and not even very good, see (my) https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption/#35614 . And you don't actually need to run openssl; Java's own cryptographic functions can do exactly the same thing, although if you don't have and use the BouncyCastle third-party libraries you need some code to put together several primitives to produce the same results as openssl's EVP_BytesToKey. There are numerous existing Qs on this, going back many years; I'll try to dig up some later. But of course you didn't ask about doing the cryptography, much less doing good cryptography, only about running the particular program specified.

CodePudding user response:

You need to be running the shell first (pipes are a shell thing) and then pass the correct program and arguments to it. Tested and working with Java 8:

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        // Set the key and iv values
        String key = "1234567812345678";
        String iv = "1234567812345678";

        // Set the string to encrypt
        String string = "passwordToBeEncrypted";

        // Create the ProcessBuilder object and specify the command and its arguments
        String command = String.format("echo %s | openssl enc -aes-256-cbc -k %s -iv %s -base64", string, key, iv);
        ProcessBuilder pb = new ProcessBuilder("bash", "-c", command);
        pb.inheritIO();

        // Start the process and get the Process object
        Process p = pb.start();


        // Wait for the process to finish and get the exit code
        int exitCode = p.waitFor();
        System.out.println("Exit code: "   exitCode);

    }
}
  • Related