From 8ef6234bc50328cd547a9538b0249dcd36b88754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sat, 23 Nov 2024 13:11:37 +0100 Subject: [PATCH] chore: Refactor how OpenSSL processes are started --- .../openssl/OpenSSLCertificateCreator.java | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index f84b1b9..5a994e2 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -18,6 +18,8 @@ import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; @@ -54,6 +56,8 @@ public class OpenSSLCertificateCreator { private static final Pattern FINGERPRINT_EXTRACTOR = Pattern.compile( "^(?[0-9a-zA-Z]+) (?i)Fingerprint(?-i)=(?[a-z:A-Z0-9]+)" ); + private static final String OSSL_ENV_KEY_PW = "KEY_PASS"; + private static final String OSSL_ARG_KEY_PW = "env:" + OSSL_ENV_KEY_PW; private final AtomicBoolean versionLogged = new AtomicBoolean(false); private final ExecutableResolver executableResolver; @@ -78,6 +82,25 @@ public class OpenSSLCertificateCreator { return certSubject; } + private static void killIfActive(StartedProcess process) { + if (process == null) { + return; + } + Process sysProc = process.getProcess(); + if (!sysProc.isAlive()) { + return; + } + + try { + log.debug("Process is still alive. Asking politely for it to destroy itself and waiting on exit for 2s"); + sysProc.destroy(); + sysProc.waitFor(2_000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + log.debug("Interrupted while waiting for process to terminate. Registering forceful termination onExit", e); + Runtime.getRuntime().addShutdownHook(new Thread(sysProc::destroyForcibly)); + } + } + @NonNull public OpenSSLCertificateResult createCertificate(CertificateRequest request) throws CommandLineOperationException, InterruptedException { @@ -88,28 +111,28 @@ public class OpenSSLCertificateCreator { throw new CommandLineOperationException("Could not create temporary directory for certificate creation", e); } - String keypassphrase = passwordProvider.generateNewPassword(); - Path keyFile = createKeyfile(request, tmpDir.resolve("certificate.key"), keypassphrase); + String keyPassphrase = passwordProvider.generateNewPassword(); + Path keyFile = createKeyfile(request, tmpDir.resolve("certificate.key"), keyPassphrase); if ( request.getType() == RequestType.ROOT_AUTHORITY || request.getType() == RequestType.STANDALONE_CERTIFICATE ) { - Path certificate = createCertificate(request, keyFile, tmpDir.resolve("certificate.crt"), keypassphrase); + Path certificate = createCertificate(request, keyFile, tmpDir.resolve("certificate.crt"), keyPassphrase); String fingerprint = getCertificateFingerprint(certificate); - passwordProvider.setPasswordFor(fingerprint, keypassphrase); + passwordProvider.setPasswordFor(fingerprint, keyPassphrase); return new OpenSSLCertificateResult(tmpDir, certificate, keyFile, certificate, fingerprint); } try (var certAuthority = certificateProvider.requestCertificateUsage(request.getTrustingAuthority())) { - Path unsignedCert = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), keypassphrase); + Path unsignedCert = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), keyPassphrase); Path signedCert = signCertificate( request, certAuthority.certificatePath(), certAuthority.certificateKeyPath(), unsignedCert, - keypassphrase + keyPassphrase ); String fingerprint = getCertificateFingerprint(signedCert); - passwordProvider.setPasswordFor(fingerprint, keypassphrase); + passwordProvider.setPasswordFor(fingerprint, keyPassphrase); Path fullchain = tmpDir.resolve("fullchain.pem"); try { @@ -140,10 +163,10 @@ public class OpenSSLCertificateCreator { keyFile.toString(), "-aes256", "-passout", - "env:KEY_PASS", + OSSL_ARG_KEY_PW, Integer.toString(request.getRequestedKeyLength()) ) - .environment("KEY_PASS", filePassword) + .environment(OSSL_ENV_KEY_PW, filePassword) .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); @@ -156,20 +179,22 @@ public class OpenSSLCertificateCreator { return keyFile; } - private Path createCertificate(CertificateRequest request, Path keyFile, Path outFile, String certPassword) + private Path createCertificate(CertificateRequest request, Path keyFile, Path outFile, String keyPassphrase) throws CommandLineOperationException, InterruptedException { log.debug("Writing new certificate file {}", outFile); String certSubject = buildSubjectArg(request); + StartedProcess certGenProc = null; try { - StartedProcess certGenProc = new ProcessExecutor() + certGenProc = + new ProcessExecutor() .command( resolveOpenSSL(), "req", "-x509", "-new", "-passin", - "env:KEY_PASS", + OSSL_ARG_KEY_PW, "-key", keyFile.toString(), "-sha256", @@ -177,13 +202,11 @@ public class OpenSSLCertificateCreator { Integer.toString(request.getRequestedValidityDays()), "-out", outFile.toString(), - "-passout", - "env:KEY_PASS", "-utf8", "-subj", certSubject ) - .environment("KEY_PASS", certPassword) + .environment(OSSL_ENV_KEY_PW, keyPassphrase) .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); @@ -192,6 +215,8 @@ public class OpenSSLCertificateCreator { throw new CommandLineOperationException("Failure running OpenSSL req command.", e); } catch (ExecutionException e) { throw new RuntimeException(e); + } finally { + killIfActive(certGenProc); } return outFile; } @@ -208,19 +233,17 @@ public class OpenSSLCertificateCreator { "req", "-new", "-passin", - "env:KEY_PASS", + OSSL_ARG_KEY_PW, "-key", keyFile.toString(), "-sha256", "-out", outFile.toString(), - "-passout", - "env:KEY_PASS", "-utf8", "-subj", certSubject ) - .environment("KEY_PASS", certPassword) + .environment(OSSL_ENV_KEY_PW, certPassword) .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); @@ -393,8 +416,8 @@ public class OpenSSLCertificateCreator { .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - certGenProc.getFuture().get(); - } catch (IOException e) { + certGenProc.getFuture().get(30, TimeUnit.SECONDS); + } catch (IOException | TimeoutException e) { throw new CommandLineOperationException("Failure running OpenSSL x509 command.", e); } catch (ExecutionException e) { throw new RuntimeException(e);