【发布时间】:2022-10-01 09:42:56
【问题描述】:
我正在尝试自动化手动生成 .p12 签名证书以进行 Web 授权的过程。
KeyPair (.p12) 和 CertReq (.csr) 文件是使用 JDK 的 keytool 程序生成的。现在我需要使用 OpenSSL 使用中间证书签署 .p12,但与在 ProcessBuilder 或 Runtime.getRuntime().exec(...) 中执行 \"keytool\" 命令不同,openssl 很难通过 Java\ 的 Process 对象工作。我不知道它有什么问题。
我需要执行的命令:
openssl ca -config ./CA_config.cnf -extensions my_client_cert -infiles ./CA_certreqs/MY_CLIENT_AAAAAA.csr
它有 3 个时刻等待用户输入并在它们之间输出文本:
- 输入CA_certificate.crt密码(CA_config.cnf指向的.crt文件);
- 签署证书 - [y/n];
- 提交结果 - [y/n]。
下面提供了代码快照。大多数 vars 都被硬代码替换,以便于阅读。
private static void signCertReqWithOpenSSL2() throws IOException { String command = \"openssl ca -config ./CA_config.cnf -extensions my_client_cert -infiles ./CA_certreqs/MY_CLIENT_AAAAAA.csr\" String[] commandSeparated = command.split(\" \"); //init cmd process ProcessBuilder pb = new ProcessBuilder(\"cmd.exe\"); pb.redirectErrorStream(true); pb.directory(new File(\"../dir1/dir2/\").getAbsoluteFile()); pb.command(commandSeparated); Process process = pb.start(); try (InputStream in = process.getInputStream()); OutputStream out = process.getOutputStream()) { System.out.println(\"--- begin---\"); readAllConsoleOutputFromBuffer(in, 80); //93 bytes actually //enter CA_certificate.crt password enterUserInputToOutputStream(out, caPassword); readAllConsoleOutputFromBuffer(in, 10); //350 //sign the certificate enterUserInputToOutputStream(out, \"y\"); readAllConsoleOutputFromBuffer(in, 10); //56 //commit the certification enterUserInputToOutputStream(out, \"y\"); readAllConsoleOutputFromBuffer(process.getInputStream(), 10); //4815 System.out.println(\"--- end ---\"); } process.destroy(); } private static void enterUserInputToOutputStream(OutputStream out, String input) throws IOException { out.write(String.format(\"%s%n\", input).getBytes()); out.flush(); } //if the stream has enough text to be printed (indicating that it\'s probably ready for user input), print it private static void readAllConsoleOutputFromBuffer(InputStream in, int minTextSizeInBytes) throws IOException { //loop is made just to make it scanning the stream during some time. I know there\'re better ways for (int i = 0; i < 100000; i++) { if (in.available() > minTextSizeInBytes) { String line; BufferedReader buff = new BufferedReader(new InputStreamReader(in)); while ((line = buff.readLine()) != null) { System.out.println(line); } break; } } }问题: 我无法让它结束,因此它会生成一个新的 .pem 文件和/或在控制台中输出“BEGIN CERTIFICATE”文本以供我进一步处理。
它甚至没有到达我需要输入 CA_certificate.crt 密码的第一个输入点。 充其量,我抓住了第一行输出“使用来自 ./CA_config.cnf 的配置”。
我确定一切都设置得很好。
- openssl 目录存在于 %PATH% 中;
- 所有文件和文件夹都存在并且 OpenSSL 会找到它们(如果我在 CA_config.cnf 中出错或删除了执行所需的任何文件,我会在控制台输出中捕获错误,即未找到某些内容)。
我尝试过的:
- 忽略控制台输出(与 InputStream 交互);
- 等待一段时间的各种方式,因此 openssl 将准备好使用来自我的输入(Thread.sleep、其他线程检查条件或睡眠、for 循环以使时间流逝等);
- 使用 openssl.exe 作为可执行文件而不是 cmd.exe - 我重写了命令和 CA_config.cnf 中的路径,得到了与 cmd.exe 及其相对路径相同的结果。
- 弄乱字符串和编码,以防它在读取第一行输出后以某种方式卡在行终止符上,即使我怀疑这是根本原因。
除了将命令委派给 .bat 文件之外,任何帮助或想法如何使工作正常?也许我没有以正确的方式与 Process 对象的输入和输出流交互。
任何帮助表示赞赏!
操作系统:Windows 10 x64
-
如果你使用纯 java 来做到这一点呢?我敢打赌 bouncycastle 具有所有必需的功能。另外你为什么使用
cmd.exe?new ProcessBuilder(\"java\", \"-version\")等应该直接工作。可能是superuser.com/a/1258521 (cmd.exe /c)。最后,检查[Process.html#getErrorStream()],很可能有你的原因 -
啊,你重定向错误,所以不是这样。但是如果我正确阅读文档
new Pb(\'cmd1\').setCommand(\'cmd2\')最终将命令设置为\'cmd2\'并忽略最初的 -
@zalp 嗯...那肯定是盲目的。我很惊讶我的“keytool”命令如何运作良好。现在我得到一个错误\“
\'A_config.cnf\' is not recognized as an internal or external command\”。 PB 可能在 \"-config ./CA_config.cnf\" 部分中提取了 cmd\ 的 \"/C\" 标志。所以现在我需要弄清楚如何逃避这种情况。 -
如果您想运行
cmd /c java -version,可能需要执行(\"cmd\", \"/c\", \"java -version\"),以便 cmd 将您要执行的内容视为单个命令,而不是多个命令。如果没有cmd,情况可能会有所不同,因为现在java想知道它的论点是什么
标签: java cmd openssl inputstream processbuilder