Java - 在运行时执行 OpenSSL 命令时出错

Java - Error in executing OpenSSL command thorugh Runtime

我有以下代码来执行 Openssl 命令并通过 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);
        }
        
    }

当我通过 Cmd 运行 命令时,它正在运行并且我能够看到输出

但是当我 运行 通过此代码时,出现以下错误:

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

注意:我已经在路径变量中设置了 bin 文件夹

我能够 运行 在 cmd 中执行命令并获得输出。我的要求是从命令 ouptut

中获取标准输入值

非常感谢您的帮助!

首先,您显然在 PATH 环境变量的任何目录中都没有 openssl[.exe]。要么更改您的 PATH 以包括包含 openssl 可执行文件的目录,要么更改您的代码以指定 openssl 可执行文件的完整路径名(因此它不需要使用 PATH)。如果在 Windows 上,请注意您可以在单个进程中或在注册表中全局更改 PATH;在后一种情况下,您需要 重新创建 使用 PATH 的任何进程,例如您的 'command' window、终端、shell、电源shell,或 IDE。欺骗 Java exec can't run program, error = 2 and CreateProcess error=2, The system cannot find the file specified .

其次,您拼写错误 -modulus

第三,将像 "|",etc 这样的重定向作为参数传递给 openssl 是行不通的。当您在 Unix 中向 shell 或 Windows 中的命令提示符键入 openssl rsa ... | openssl sha256 时,不会将 | openssl sha256 传递给第一个 openssl;相反,它 运行 是两个不同的进程,并将第一个进程的输出重定向到一个管道,并将第二个进程的输入重定向到该管道。 Runtime.exec() 不是命令处理器,不能执行此操作。您可以:

  • 运行 仅 openssl rsa 部分,读取 Java 代码中的输出,并在 Java[ 中对该输出执行 SHA256 =24=]

  • 运行一个命令解释器并告诉执行复合命令openssl rsa ... | openssl sha256(全部作为一个 字符串)。在 Unix 上使用适当的 shell -- 这可能因您的系统而异,但 /bin/sh -c 是常见的。在 Windows 上使用 cmd.exe /c.

仅供参考,如果您拥有或获得 BouncyCastle 库,它们可以读取相同的文件 openssl rsa 可以,并提取模数并对其进行格式化和散列,所有这些都直接在 Java 中而不使用 openssl 完全没有。但是你没有问这个,所以在 Stack 上它不是一个答案。

我的系统是 Windows 10 x64,所以您可能需要调整 openssl.exe 的路径。

开始解决路径我首先运行 OpenSSL-命令行:

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

给出这个结果作为参考:

(stdin)= addc742adb857539b5b7240459e4341b8de7575b437459aacfab605dc46b5e9f

请注意,我使用的是“modulus”,而不是您的命令字符串中的“modules”。

开始 Java 体验,我了解到 opensll-command line 运行s 在 shell 中,但在 运行ning 时没有这样做 通过 Runtime.exec 的 OpenSSL。因此,有一种方法可以将完整的命令行拆分为两个单独的命令,通过 Output -> InputStream 组合在一起。在 Apache 的 common-io-library 的帮助下,有一个非常简单的解决方案可以做到这一点。您从 https://mvnrepository.com/artifact/commons-io/commons-io 获取库并 我使用 2.7 版进行测试。

以下示例取自 ,由用户@Bohemian 编写,并给出以下结果:

[(stdin)= addc742adb857539b5b7240459e4341b8de7575b437459aacfab605dc46b5e9f]

这应该是预期的结果,只需对字符串进行一些格式化,您就可以得到想要的结果。

这里是源代码,请记住没有适当的异常处理。

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("

        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"};

        // 
        // 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);
    }
}