0

I have the following code to execute Openssl command and read the ouptut produced by the command through Java Runtime

public void executeCmd() throws IOException {
        Runtime rt = Runtime.getRuntime();
        String[] commands = new String[]{"openssl", "rsa", "-noout", "-modules", "-in", "myPathToKeyFile", "|", "openssl", "sha256"};
        
        Process proc = rt.exec(commands);
        BufferedReader stdInKey = new BufferedReader(new InputStreamReader(proc.getInputStream()));

         String s = null;
        
        while ((s = stdInKey.readLine()) != null) {
            System.out.println(s);
        }
        
    }

When i run the command through Cmd, it is working and I am able to see the output

But When I run through this code, I am getting following error :

Exception in thread "main" java.io.IOException: Cannot run program "openssl": CreateProcess error=2, The system cannot find the file specified
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
    at java.lang.Runtime.exec(Runtime.java:620)
    at java.lang.Runtime.exec(Runtime.java:485)
    at com.renault.vnext.business.impl.CmdRunner.executeCmd(CmdRunner.java:22)
    at com.renault.vnext.business.impl.CmdRunner.main(CmdRunner.java:10)
Caused by: java.io.IOException: CreateProcess error=2, The system cannot find the file specified
    at java.lang.ProcessImpl.create(Native Method)
    at java.lang.ProcessImpl.<init>(ProcessImpl.java:444)
    at java.lang.ProcessImpl.start(ProcessImpl.java:140)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
    ... 4 more

Note : I have set upto bin folder in the path variable

I am able to run the command in cmd and get the output. My requirement is to get the stdin value from the command ouptut

enter image description here

Much appreciating your help!

code-geek
  • 441
  • 1
  • 8
  • 22

2 Answers2

2

First, you apparently don't have openssl[.exe] in any of the directories in your PATH environment variable. Either change your PATH to include the directory containing the openssl executable, or change your code to specify the full pathname of the openssl executable (so it doesn't need to use PATH). If on Windows, note you can change PATH in a single process, or globally in the registry; in the latter case, you need to re-create any process using PATH such as your 'command' window, terminal, shell, powershell, or IDE. Dupe Java exec can't run program, error = 2 and CreateProcess error=2, The system cannot find the file specified .

Second, you misspelled -modulus.

Third, passing a redirection like "|",etc as arguments to openssl won't work. When you type openssl rsa ... | openssl sha256 to a shell in Unix or the command prompt in Windows, that does NOT pass | openssl sha256 to the first openssl; instead it runs two different processes and redirects the output of the first to a pipe and the input of the second from that pipe. Runtime.exec() is NOT a command processor and cannot do this. You can:

  • run only the openssl rsa part, read the output in your Java code, and do the SHA256 of that output in Java

  • run a command interpreter and tell it to execute the compound command openssl rsa ... | openssl sha256 (all as one string). On Unix use an appropriate shell -- this may vary depending on your system but /bin/sh -c is common. On Windows use cmd.exe /c.

FYI, if you have or get the BouncyCastle libraries, they can read the same files openssl rsa can, and extract the modulus and format and hash it, all directly in Java without using openssl at all. But you didn't ask about that, so on Stack it's not an answer.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • Thanks for your input. Despite adding the PATH containing openssl executable upto bin folder(C:\Users\PublicSoftware_Installation\openssl-1.0.2j-fips-x86_64\OpenSSL\bin), I am facing issue. Could you please share a working code example ? – code-geek Jul 31 '20 at 05:17
  • Also I have not misspelled modulus to modules. When I run the same commnd in cmd, I get the ouptput – code-geek Jul 31 '20 at 05:23
  • 1
    On my system with PATH set correctly the code you posted works -- at least as far as running openssl. Try outputting `System.getenv("PATH")` to make sure it's set to what you think it is. `-modules` is not a valid option, and your 'working' CMD example is an error message; just read it. This error causes openssl to output _nothing_ to the pipe, so even though you obscured the hash I know it was `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` becauase that will be the output of your command for _every_ input file _every_ time _always_. ... – dave_thompson_085 Aug 04 '20 at 09:03
  • 1
    ... I assumed you wanted to do something useful, and hashing the modulus would be useful, but what you are doing isn't; if you really want the hash of nothing you don't need to run openssl at all, just use the constant value. – dave_thompson_085 Aug 04 '20 at 09:03
  • Very useful answer, thanks. I didn't know I had to restart the IDE (IntelliJ) to update/refresh the PATH used when running process. – Brian O Carroll Mar 30 '22 at 15:28
1

My system is Windows 10 x64 so maybe you have to adjust the paths to your openssl.exe.

Starting the solution path I firstly run the OpenSSL-command line:

openssl rsa -noout -modulus -in keyunencrypted.pem | openssl sha256

which gives this result as a reference:

(stdin)= addc742adb857539b5b7240459e4341b8de7575b437459aacfab605dc46b5e9f

Please note that I'm using "modulus" and not "modules" as in your commands-string.

Starting the Java experience I learned that opensll-command line runs in a shell but that's not doing when running OpenSSL via Runtime.exec. Therefore, there is one way to split the complete command line into two separate commands combined via Output -> InputStream. There is a very simple solution for doing this with the help of Apache's common-io-library. You get the library from https://mvnrepository.com/artifact/commons-io/commons-io and I used version 2.7 for my test.

The following example is taken from https://stackoverflow.com/a/7226858/8166854 and was written by User @Bohemian and gives the following result:

[(stdin)= addc742adb857539b5b7240459e4341b8de7575b437459aacfab605dc46b5e9f]

This should be the expected result and with a little string-formatting you get what you want.

Here is the source code, please keep in mind that there is no proper exception handling.

import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class MainSo {
    public static void main(String[] args) throws IOException {
        System.out.println("https://stackoverflow.com/questions/63177584/java-error-in-executing-openssl-command-thorugh-runtime/63182609#63182609");

        String[] commands1 = new String[]{"E:\\intellij_projects\\openssl_1_1_1g-win64-mingw\\openssl.exe",
        "rsa", "-noout", "-modulus", "-in", "keyunencrypted.pem"};
        String[] commands2 = new String[]{"E:\\intellij_projects\\openssl_1_1_1g-win64-mingw\\openssl.exe", "sha256"};

        // https://stackoverflow.com/a/7226858/8166854
        // by @Bohemian
        // you need https://mvnrepository.com/artifact/commons-io/commons-io
        // commons-io-2.7.jar
        Process p1 = Runtime.getRuntime().exec(commands1);
        InputStream input = p1.getInputStream();
        Process p2 = Runtime.getRuntime().exec(commands2);
        OutputStream output = p2.getOutputStream();
        IOUtils.copy(input, output);
        output.close(); // signals command2 to finish
        List<String> result = IOUtils.readLines(p2.getInputStream(), StandardCharsets.UTF_8);
        System.out.println(result);
    }
}
Michael Fehr
  • 5,827
  • 2
  • 19
  • 40