【问题标题】:Java - Error in executing OpenSSL command thorugh RuntimeJava - 通过运行时执行 OpenSSL 命令时出错
【发布时间】:2020-07-30 16:35:23
【问题描述】:

我有以下代码来执行 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 中运行命令并获得输出。我的要求是从命令输出中获取标准输入值

非常感谢您的帮助!

【问题讨论】:

    标签: java openssl


    【解决方案1】:

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

    启动解决方案路径我首先运行 OpenSSL 命令行:

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

    给出这个结果作为参考:

    (stdin)= addc742adb857539b5b7240459e4341b8de7575b437459aacfab605dc46b5e9f
    

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

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

    以下示例取自https://stackoverflow.com/a/7226858/8166854,由用户@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("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);
        }
    }
    

    【讨论】:

      【解决方案2】:

      首先,您的 PATH 环境变量中的任何目录中显然都没有 openssl[.exe]。更改您的 PATH 以包含包含 openssl 可执行文件的目录,或更改您的代码以指定 openssl 可执行文件的完整路径名(因此它不需要使用 PATH)。如果在 Windows 上,请注意您可以在单个进程中更改 PATH,也可以在注册表中全局更改;在后一种情况下,您需要重新创建任何使用 PATH 的进程,例如“命令”窗口、终端、shell、powershell 或 IDE。欺骗Java exec can't run program, error = 2CreateProcess 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

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

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

      【讨论】:

      • 感谢您的意见。尽管将包含 openssl 可执行文件的 PATH 添加到 bin 文件夹(C:\Users\PublicSoftware_Installation\openssl-1.0.2j-fips-x86_64\OpenSSL\bin),但我遇到了问题。你能分享一个工作代码示例吗?
      • 我也没有将模数拼错成模块。当我在 cmd 中运行相同的命令时,我得到了输出
      • 在我的 PATH 设置正确的系统上,您发布的代码可以正常工作——至少就运行 o​​penssl 而言。尝试输出System.getenv("PATH") 以确保它设置为您认为的那样。 -modules 不是有效选项,您的“工作”CMD 示例是一条错误消息;只是阅读它。此错误导致 openssl 将 nothing 输出到管道,因此即使您隐藏了哈希我知道它是 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 因为这将是您的命令的输出 every输入文件每次时间总是。 ...
      • ...我假设您想做一些有用的事情,并且散列模数会很有用,但您正在做的不是;如果你真的想要什么都不需要运行 openssl 的哈希值,只需使用常量值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多