From 334d97c883683ceba394126d8495b6af50d44b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sat, 23 Nov 2024 12:15:14 +0100 Subject: [PATCH 01/10] feat: Implement method for checking pKey encryption passphrase --- .../openssl/OpenSSLCertificateCreator.java | 38 +++++++++++++++++++ .../TestOpenSSLCertificateCreator.java | 18 ++++++--- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 8b8b6e8..b3d1007 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -25,6 +25,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.zeroturnaround.exec.ProcessExecutor; @@ -281,6 +282,43 @@ public class OpenSSLCertificateCreator { } } + public boolean isKeyEncrypted(@NonNull Path keyFile) throws InterruptedException, CommandLineOperationException { + // If any random passphrase works, the key is not encrypted. + return !verifyKeyPassphrase(keyFile, null); + } + + /** + * Verifies a passphrase against a provided key. + * @implNote Due to the implementation of the OpenSSL cli, any password will be valid for unencrypted keys. (Check with {@link #isKeyEncrypted(Path) or by passing {@code null} as the passphrase.) + */ + public boolean verifyKeyPassphrase(@NonNull Path keyFile, @Nullable String passphrase) + throws CommandLineOperationException, InterruptedException { + // Run OpenSSL command: openssl rsa -check -in -passin pass: -noout and check exit code + try { + String keyPass = passphrase != null ? passphrase : passwordProvider.generateNewPassword(); + StartedProcess verifyCommand = new ProcessExecutor() + .command( + resolveOpenSSL(), + "rsa", + "-check", + "-in", + keyFile.toString(), + "-passin", + "pass:" + keyPass, + "-noout" + ) + .redirectOutput(Slf4jStream.ofCaller().asError()) + .redirectError(Slf4jStream.ofCaller().asError()) + .start(); + var verifyResult = verifyCommand.getFuture().get(); + boolean commandSuccess = verifyResult.getExitValue() == 0; + log.trace("Key check {} with passphrase provided={}", commandSuccess, passphrase != null); + return commandSuccess; + } catch (IOException | InterruptedException | ExecutionException e) { + throw new CommandLineOperationException("Failed to verify key encryption", e); + } + } + private Path signCertificate( CertificateRequest request, Path caCert, diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java index bf5de81..5c1da75 100644 --- a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java @@ -14,6 +14,8 @@ class TestOpenSSLCertificateCreator { public static final String TEST_CERT_PASSPHRASE = "ABC-123"; public static final Path TEST_CERT_PATH = Path.of("src/test/resources/openssl"); + public static final String ERR_NOT_ENCRYPTED = "Private key not encrypted"; + public static final String ERR_VERIFY_FAILED = "Certificate verification failed"; private CertificatePasswordProvider passwordProvider; @BeforeEach @@ -50,8 +52,11 @@ class TestOpenSSLCertificateCreator { try (var cert = certificateCreator.createCertificate(certRequest)) { assertThat(certificateCreator.verifyCertificate(cert.certificatePath(), cert.certificatePath())) - .isEqualTo(true); - System.out.println("Certificate created: " + cert); + .withFailMessage(ERR_VERIFY_FAILED) + .isTrue(); + assertThat(certificateCreator.isKeyEncrypted(cert.certificateKeyPath())) + .withFailMessage(ERR_NOT_ENCRYPTED) + .isTrue(); CertificateRequest childRequest = CertificateRequest .builder() @@ -73,12 +78,15 @@ class TestOpenSSLCertificateCreator { doNothing().when(spiedCert).close(); when(certificateProvider.requestCertificateUsage(cert.fingerprint())).thenReturn(spiedCert); try (var childCert = certificateCreator.createCertificate(childRequest)) { - System.out.println("Child certificate created: " + childCert); Path fullchain = childCert.fullchainPath(); assertThat( - certificateCreator.verifyCertificate(cert.certificatePath(), Objects.requireNonNull(fullchain)) + certificateCreator.verifyCertificate(Objects.requireNonNull(fullchain), cert.certificatePath()) ) - .isEqualTo(true); + .withFailMessage(ERR_VERIFY_FAILED) + .isTrue(); + assertThat(certificateCreator.isKeyEncrypted(childCert.certificateKeyPath())) + .withFailMessage(ERR_NOT_ENCRYPTED) + .isTrue(); } } } -- 2.45.3 From 5da1e5894d039b4d9becb97fbbdd24759d4fb078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sat, 23 Nov 2024 12:36:38 +0100 Subject: [PATCH 02/10] feat: Verify key is unencrypted using two random passphrases --- .../openssl/OpenSSLCertificateCreator.java | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index b3d1007..18730c3 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -15,6 +15,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -25,7 +26,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.zeroturnaround.exec.ProcessExecutor; @@ -135,6 +135,7 @@ public class OpenSSLCertificateCreator { "genrsa", "-out", keyFile.toString(), + "-aes256", "-passout", "env:KEY_PASS", Integer.toString(request.getRequestedKeyLength()) @@ -282,20 +283,28 @@ public class OpenSSLCertificateCreator { } } + /** + * Checks whether the provided key file is encrypted using a passphrase + */ public boolean isKeyEncrypted(@NonNull Path keyFile) throws InterruptedException, CommandLineOperationException { - // If any random passphrase works, the key is not encrypted. - return !verifyKeyPassphrase(keyFile, null); + // If the key is not encrypted, any passphrase will work -> so generate a random one to check. + String passphrase = UUID.randomUUID().toString(); + boolean firstPass = verifyKeyPassphrase(keyFile, passphrase); + if (firstPass) { + // Try with another random passphrase in case we randomly got lucky guessing the passphrase the first time. + passphrase = UUID.randomUUID().toString(); + return !verifyKeyPassphrase(keyFile, passphrase); + } + return true; } /** * Verifies a passphrase against a provided key. - * @implNote Due to the implementation of the OpenSSL cli, any password will be valid for unencrypted keys. (Check with {@link #isKeyEncrypted(Path) or by passing {@code null} as the passphrase.) + * @implNote Due to the implementation of the OpenSSL cli, any password will be valid for unencrypted keys. (Check with {@link #isKeyEncrypted(Path).) */ - public boolean verifyKeyPassphrase(@NonNull Path keyFile, @Nullable String passphrase) + public boolean verifyKeyPassphrase(@NonNull Path keyFile, @NonNull String passphrase) throws CommandLineOperationException, InterruptedException { - // Run OpenSSL command: openssl rsa -check -in -passin pass: -noout and check exit code try { - String keyPass = passphrase != null ? passphrase : passwordProvider.generateNewPassword(); StartedProcess verifyCommand = new ProcessExecutor() .command( resolveOpenSSL(), @@ -304,16 +313,14 @@ public class OpenSSLCertificateCreator { "-in", keyFile.toString(), "-passin", - "pass:" + keyPass, + "pass:" + passphrase, "-noout" ) .redirectOutput(Slf4jStream.ofCaller().asError()) .redirectError(Slf4jStream.ofCaller().asError()) .start(); var verifyResult = verifyCommand.getFuture().get(); - boolean commandSuccess = verifyResult.getExitValue() == 0; - log.trace("Key check {} with passphrase provided={}", commandSuccess, passphrase != null); - return commandSuccess; + return verifyResult.getExitValue() == 0; } catch (IOException | InterruptedException | ExecutionException e) { throw new CommandLineOperationException("Failed to verify key encryption", e); } -- 2.45.3 From 344e80e5acedecfe7d02e89fe98f87419fc8a55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sat, 23 Nov 2024 12:41:36 +0100 Subject: [PATCH 03/10] feat: Move OpenSSL output to custom logger --- .../openssl/OpenSSLCertificateCreator.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 18730c3..9b5d06e 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -1,6 +1,7 @@ package de.mlessmann.certassist.openssl; import static de.mlessmann.certassist.Constants.CERTASSIST_TMP_PREFIX; +import static org.slf4j.LoggerFactory.getLogger; import de.mlessmann.certassist.DeleteRecursiveFileVisitor; import de.mlessmann.certassist.ExecutableResolver; @@ -25,6 +26,7 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -37,6 +39,7 @@ import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; @Slf4j public class OpenSSLCertificateCreator { + private static final Logger openSSLLogger = getLogger("OpenSSL-Logger"); public static final String OPENSSL_CERT_SUBJECT_TEMPLATE = "/C=ISO-COUNTRY/ST=STATE/L=LOCALITY/O=ORGANIZATION/CN=COMMON-NAME"; private static final String CSR_EXT_TEMPLATE = @@ -141,8 +144,8 @@ public class OpenSSLCertificateCreator { Integer.toString(request.getRequestedKeyLength()) ) .environment("KEY_PASS", filePassword) - .redirectOutput(Slf4jStream.ofCaller().asDebug()) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); keygenProc.getFuture().get(); } catch (IOException e) { @@ -181,8 +184,8 @@ public class OpenSSLCertificateCreator { certSubject ) .environment("KEY_PASS", certPassword) - .redirectOutput(Slf4jStream.ofCaller().asDebug()) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); certGenProc.getFuture().get(); } catch (IOException e) { @@ -218,8 +221,8 @@ public class OpenSSLCertificateCreator { certSubject ) .environment("KEY_PASS", certPassword) - .redirectOutput(Slf4jStream.ofCaller().asDebug()) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); certGenProc.getFuture().get(); } catch (IOException e) { @@ -264,8 +267,8 @@ public class OpenSSLCertificateCreator { StartedProcess verifyCommand = new ProcessExecutor() .command(resolveOpenSSL(), "verify", "-CAfile", tempTrustedBundle.toString(), fullChainFile.toString()) - .redirectOutput(Slf4jStream.ofCaller().asError()) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectOutput(Slf4jStream.of(openSSLLogger).asError()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); var verifyResult = verifyCommand.getFuture().get(); return verifyResult.getExitValue() == 0; @@ -316,8 +319,8 @@ public class OpenSSLCertificateCreator { "pass:" + passphrase, "-noout" ) - .redirectOutput(Slf4jStream.ofCaller().asError()) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectOutput(Slf4jStream.of(openSSLLogger).asError()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); var verifyResult = verifyCommand.getFuture().get(); return verifyResult.getExitValue() == 0; @@ -387,8 +390,8 @@ public class OpenSSLCertificateCreator { "-extfile", extFile.toString() ) - .redirectOutput(Slf4jStream.ofCaller().asDebug()) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); certGenProc.getFuture().get(); } catch (IOException e) { @@ -405,7 +408,7 @@ public class OpenSSLCertificateCreator { StartedProcess fingerprintProc = new ProcessExecutor() .command(resolveOpenSSL(), "x509", "-in", certificate.toString(), "-noout", "-fingerprint") .readOutput(true) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); var fingerprintResult = fingerprintProc.getFuture().get(); String output = fingerprintResult.getOutput().getUTF8(); @@ -466,7 +469,7 @@ public class OpenSSLCertificateCreator { "lname" ) .readOutput(true) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); var infoResult = infoProc.getFuture().get(); String output = infoResult.getOutput().getUTF8(); @@ -490,7 +493,7 @@ public class OpenSSLCertificateCreator { StartedProcess versionProc = new ProcessExecutor() .command(path, "version") .readOutput(true) - .redirectError(Slf4jStream.ofCaller().asError()) + .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); var versionResult = versionProc.getFuture().get(); if (versionResult.getExitValue() != 0) { -- 2.45.3 From c24adcbcd653b5d5389282702742e1699389445a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sat, 23 Nov 2024 12:50:56 +0100 Subject: [PATCH 04/10] chore: Make TestOpenSSLCertificateCreator a spring boot test --- .../openssl/OpenSSLCertificateCreator.java | 18 +++++++++--------- .../TestOpenSSLCertificateCreator.java | 11 ++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 9b5d06e..f84b1b9 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -88,28 +88,28 @@ public class OpenSSLCertificateCreator { throw new CommandLineOperationException("Could not create temporary directory for certificate creation", e); } - String certPassword = passwordProvider.generateNewPassword(); - Path keyFile = createKeyfile(request, tmpDir.resolve("certificate.key"), certPassword); + 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"), certPassword); + Path certificate = createCertificate(request, keyFile, tmpDir.resolve("certificate.crt"), keypassphrase); String fingerprint = getCertificateFingerprint(certificate); - passwordProvider.setPasswordFor(fingerprint, certPassword); + 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"), certPassword); + Path unsignedCert = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), keypassphrase); Path signedCert = signCertificate( request, certAuthority.certificatePath(), certAuthority.certificateKeyPath(), unsignedCert, - certPassword + keypassphrase ); String fingerprint = getCertificateFingerprint(signedCert); - passwordProvider.setPasswordFor(fingerprint, certPassword); + passwordProvider.setPasswordFor(fingerprint, keypassphrase); Path fullchain = tmpDir.resolve("fullchain.pem"); try { @@ -129,7 +129,7 @@ public class OpenSSLCertificateCreator { private Path createKeyfile(CertificateRequest request, Path outFile, String filePassword) throws CommandLineOperationException, InterruptedException { Path keyFile = outFile.toAbsolutePath(); - log.atDebug().log("Writing new certificate key to {}", keyFile); + log.debug("Writing new certificate key to {}", keyFile); try { StartedProcess keygenProc = new ProcessExecutor() @@ -158,7 +158,7 @@ public class OpenSSLCertificateCreator { private Path createCertificate(CertificateRequest request, Path keyFile, Path outFile, String certPassword) throws CommandLineOperationException, InterruptedException { - log.atDebug().log("Writing new certificate file {}", outFile); + log.debug("Writing new certificate file {}", outFile); String certSubject = buildSubjectArg(request); try { diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java index 5c1da75..c39a807 100644 --- a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java @@ -9,14 +9,23 @@ import java.nio.file.Path; import java.util.Objects; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +@SpringBootTest class TestOpenSSLCertificateCreator { public static final String TEST_CERT_PASSPHRASE = "ABC-123"; public static final Path TEST_CERT_PATH = Path.of("src/test/resources/openssl"); public static final String ERR_NOT_ENCRYPTED = "Private key not encrypted"; public static final String ERR_VERIFY_FAILED = "Certificate verification failed"; - private CertificatePasswordProvider passwordProvider; + + @Autowired + OpenSSLCertificateCreator openSSLCertificateCreator; + + @MockBean + CertificatePasswordProvider passwordProvider; @BeforeEach void setUp() { -- 2.45.3 From c57f0a146265024e34df553367e8d3ca1e210fe1 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 05/10] 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); -- 2.45.3 From d801e151ad3d06e0a0508754314c8fe0d581b3c9 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:16:44 +0100 Subject: [PATCH 06/10] fix: Retry cleanup again on system exit --- .../certassist/openssl/OpenSSLCertificateResult.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java index bbca3f5..036b9b7 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java @@ -41,12 +41,20 @@ public class OpenSSLCertificateResult implements CertificateUsage { @Override public void close() { + cleanupDir(true); + } + + private void cleanupDir(boolean retryOnExit) { + try { log.info("Cleaning up temporary output directory {}", tmpDir); Files.walkFileTree(tmpDir, Set.of(), Integer.MAX_VALUE, new DeleteRecursiveFileVisitor()); Files.deleteIfExists(tmpDir); } catch (IOException e) { - log.error("Failed to clean up temporary output directory {}!", tmpDir, e); + log.error("Failed to clean up temporary output directory {}! (retry={})", tmpDir, retryOnExit, e); + if (retryOnExit) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> cleanupDir(false))); + } } } } -- 2.45.3 From 8d00a60d87c57008049d73f408301f63f8dda175 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:17:21 +0100 Subject: [PATCH 07/10] chore: Reduce logging noise from DB --- src/main/resources/application.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 115f1fb..cacc878 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,11 +8,11 @@ password=admin spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect #TODO: Use flyway for db setup hibernate.hbm2ddl.auto=update -hibernate.show_sql=true -hibernate.format_sql=true +#hibernate.show_sql=true +#hibernate.format_sql=true # Logging logging.level.root=INFO logging.level.de.mlessmann.certassist=DEBUG -logging.level.org.sqlite=TRACE -logging.level.org.hibernate=DEBUG \ No newline at end of file +#logging.level.org.sqlite=TRACE +#logging.level.org.hibernate=DEBUG \ No newline at end of file -- 2.45.3 From 022e5497d11ebe829f01944121796e8dbc8d58e7 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:20:14 +0100 Subject: [PATCH 08/10] chore: Kill genCert on error - Because apparently it can run into an infinite loop when there are pKey issues --- .../certassist/openssl/OpenSSLCertificateCreator.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 5a994e2..74af6ab 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -393,8 +393,9 @@ public class OpenSSLCertificateCreator { throw new RuntimeException(e); } + StartedProcess certGenProc = null; try { - StartedProcess certGenProc = new ProcessExecutor() + certGenProc = new ProcessExecutor() .command( resolveOpenSSL(), "x509", @@ -421,6 +422,8 @@ public class OpenSSLCertificateCreator { throw new CommandLineOperationException("Failure running OpenSSL x509 command.", e); } catch (ExecutionException e) { throw new RuntimeException(e); + } finally { + killIfActive(certGenProc); } return outFile; } -- 2.45.3 From a78f815a76b231b4352adc974f1173e7a6415259 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:31:36 +0100 Subject: [PATCH 09/10] fix: Check exit code of signing command --- .../openssl/OpenSSLCertificateCreator.java | 26 +++++++++++++------ .../openssl/OpenSSLCertificateResult.java | 1 - 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 74af6ab..ea02474 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -33,6 +33,7 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.ProcessResult; import org.zeroturnaround.exec.StartedProcess; import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; @@ -123,13 +124,13 @@ public class OpenSSLCertificateCreator { } try (var certAuthority = certificateProvider.requestCertificateUsage(request.getTrustingAuthority())) { - Path unsignedCert = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), keyPassphrase); + Path signingRequest = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), keyPassphrase); Path signedCert = signCertificate( request, certAuthority.certificatePath(), certAuthority.certificateKeyPath(), - unsignedCert, - keyPassphrase + passwordProvider.getPasswordFor(certAuthority.fingerprint()), + signingRequest ); String fingerprint = getCertificateFingerprint(signedCert); passwordProvider.setPasswordFor(fingerprint, keyPassphrase); @@ -356,11 +357,11 @@ public class OpenSSLCertificateCreator { CertificateRequest request, Path caCert, Path caKey, - Path csrFile, - String certPassword + String caKeyPassphrase, + Path csrFile ) throws CommandLineOperationException, InterruptedException { Path outFile = csrFile.resolveSibling(csrFile.getFileName().toString().replace(".csr", ".crt")); - log.atDebug().log("Writing new signed certificate file {}", outFile); + log.debug("Writing new signed certificate file {}", outFile); Path extFile = csrFile.resolveSibling(csrFile.getFileName().toString().replace(".csr", ".ext")); try { @@ -395,7 +396,8 @@ public class OpenSSLCertificateCreator { StartedProcess certGenProc = null; try { - certGenProc = new ProcessExecutor() + certGenProc = + new ProcessExecutor() .command( resolveOpenSSL(), "x509", @@ -409,15 +411,23 @@ public class OpenSSLCertificateCreator { "-CAkey", caKey.toString(), "-CAcreateserial", + "-passin", + OSSL_ARG_KEY_PW, "-out", outFile.toString(), "-extfile", extFile.toString() ) + .environment(OSSL_ENV_KEY_PW, caKeyPassphrase) .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - certGenProc.getFuture().get(30, TimeUnit.SECONDS); + ProcessResult result = certGenProc.getFuture().get(30, TimeUnit.SECONDS); + // Check exit code + if (result.getExitValue() != 0) { + throw new CommandLineOperationException("Failed to sign certificate. Exit code: " + result.getExitValue()); + } + } catch (IOException | TimeoutException e) { throw new CommandLineOperationException("Failure running OpenSSL x509 command.", e); } catch (ExecutionException e) { diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java index 036b9b7..17d1721 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java @@ -45,7 +45,6 @@ public class OpenSSLCertificateResult implements CertificateUsage { } private void cleanupDir(boolean retryOnExit) { - try { log.info("Cleaning up temporary output directory {}", tmpDir); Files.walkFileTree(tmpDir, Set.of(), Integer.MAX_VALUE, new DeleteRecursiveFileVisitor()); -- 2.45.3 From 3f7e41b245c5eba4ea9409d396bbc84b546837bc 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:41:30 +0100 Subject: [PATCH 10/10] chore: Update termination/exception handling --- .../openssl/OpenSSLCertificateCreator.java | 102 ++++++++++-------- .../service/CertificateCreationService.java | 5 - 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index ea02474..add5193 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -1,6 +1,7 @@ package de.mlessmann.certassist.openssl; import static de.mlessmann.certassist.Constants.CERTASSIST_TMP_PREFIX; +import static java.util.concurrent.TimeUnit.*; import static org.slf4j.LoggerFactory.getLogger; import de.mlessmann.certassist.DeleteRecursiveFileVisitor; @@ -18,7 +19,6 @@ 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; @@ -95,7 +95,7 @@ public class OpenSSLCertificateCreator { 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); + sysProc.waitFor(2_000, 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)); @@ -103,8 +103,7 @@ public class OpenSSLCertificateCreator { } @NonNull - public OpenSSLCertificateResult createCertificate(CertificateRequest request) - throws CommandLineOperationException, InterruptedException { + public OpenSSLCertificateResult createCertificate(CertificateRequest request) throws CommandLineOperationException { Path tmpDir; try { tmpDir = Files.createTempDirectory(CERTASSIST_TMP_PREFIX); @@ -151,12 +150,14 @@ public class OpenSSLCertificateCreator { } private Path createKeyfile(CertificateRequest request, Path outFile, String filePassword) - throws CommandLineOperationException, InterruptedException { + throws CommandLineOperationException { Path keyFile = outFile.toAbsolutePath(); log.debug("Writing new certificate key to {}", keyFile); + StartedProcess keygenProc = null; try { - StartedProcess keygenProc = new ProcessExecutor() + keygenProc = + new ProcessExecutor() .command( resolveOpenSSL(), "genrsa", @@ -171,17 +172,17 @@ public class OpenSSLCertificateCreator { .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - keygenProc.getFuture().get(); - } catch (IOException e) { + keygenProc.getFuture().get(3, MINUTES); + } catch (IOException | ExecutionException | InterruptedException | TimeoutException e) { throw new CommandLineOperationException("Failure running OpenSSL keygen command.", e); - } catch (ExecutionException e) { - throw new RuntimeException(e); + } finally { + killIfActive(keygenProc); } return keyFile; } private Path createCertificate(CertificateRequest request, Path keyFile, Path outFile, String keyPassphrase) - throws CommandLineOperationException, InterruptedException { + throws CommandLineOperationException { log.debug("Writing new certificate file {}", outFile); String certSubject = buildSubjectArg(request); @@ -211,11 +212,9 @@ public class OpenSSLCertificateCreator { .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - certGenProc.getFuture().get(); - } catch (IOException e) { + certGenProc.getFuture().get(30, SECONDS); + } catch (IOException | ExecutionException | InterruptedException | TimeoutException e) { throw new CommandLineOperationException("Failure running OpenSSL req command.", e); - } catch (ExecutionException e) { - throw new RuntimeException(e); } finally { killIfActive(certGenProc); } @@ -223,12 +222,14 @@ public class OpenSSLCertificateCreator { } private Path createSigningRequest(CertificateRequest request, Path keyFile, Path outFile, String certPassword) - throws CommandLineOperationException, InterruptedException { + throws CommandLineOperationException { log.atDebug().log("Writing new certificate signing request file {}", outFile); String certSubject = buildSubjectArg(request); + StartedProcess certGenProc = null; try { - StartedProcess certGenProc = new ProcessExecutor() + certGenProc = + new ProcessExecutor() .command( resolveOpenSSL(), "req", @@ -248,11 +249,11 @@ public class OpenSSLCertificateCreator { .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - certGenProc.getFuture().get(); - } catch (IOException e) { + certGenProc.getFuture().get(30, SECONDS); + } catch (IOException | ExecutionException | InterruptedException | TimeoutException e) { throw new CommandLineOperationException("Failure running OpenSSL req command.", e); - } catch (ExecutionException e) { - throw new RuntimeException(e); + } finally { + killIfActive(certGenProc); } return outFile; } @@ -271,6 +272,7 @@ public class OpenSSLCertificateCreator { } Path tmpDir = null; + StartedProcess verifyCommand = null; try { Path tempTrustedBundle; if (trustedCAs.size() == 1) { @@ -289,16 +291,18 @@ public class OpenSSLCertificateCreator { } } - StartedProcess verifyCommand = new ProcessExecutor() + verifyCommand = + new ProcessExecutor() .command(resolveOpenSSL(), "verify", "-CAfile", tempTrustedBundle.toString(), fullChainFile.toString()) .redirectOutput(Slf4jStream.of(openSSLLogger).asError()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - var verifyResult = verifyCommand.getFuture().get(); + var verifyResult = verifyCommand.getFuture().get(30, SECONDS); return verifyResult.getExitValue() == 0; - } catch (IOException | InterruptedException | ExecutionException e) { + } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) { throw new RuntimeException(e); } finally { + killIfActive(verifyCommand); if (tmpDir != null) { try { Files.walkFileTree(tmpDir, new DeleteRecursiveFileVisitor()); @@ -313,7 +317,7 @@ public class OpenSSLCertificateCreator { /** * Checks whether the provided key file is encrypted using a passphrase */ - public boolean isKeyEncrypted(@NonNull Path keyFile) throws InterruptedException, CommandLineOperationException { + public boolean isKeyEncrypted(@NonNull Path keyFile) throws CommandLineOperationException { // If the key is not encrypted, any passphrase will work -> so generate a random one to check. String passphrase = UUID.randomUUID().toString(); boolean firstPass = verifyKeyPassphrase(keyFile, passphrase); @@ -330,9 +334,11 @@ public class OpenSSLCertificateCreator { * @implNote Due to the implementation of the OpenSSL cli, any password will be valid for unencrypted keys. (Check with {@link #isKeyEncrypted(Path).) */ public boolean verifyKeyPassphrase(@NonNull Path keyFile, @NonNull String passphrase) - throws CommandLineOperationException, InterruptedException { + throws CommandLineOperationException { + StartedProcess verifyCommand = null; try { - StartedProcess verifyCommand = new ProcessExecutor() + verifyCommand = + new ProcessExecutor() .command( resolveOpenSSL(), "rsa", @@ -346,10 +352,12 @@ public class OpenSSLCertificateCreator { .redirectOutput(Slf4jStream.of(openSSLLogger).asError()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - var verifyResult = verifyCommand.getFuture().get(); + var verifyResult = verifyCommand.getFuture().get(30, SECONDS); return verifyResult.getExitValue() == 0; - } catch (IOException | InterruptedException | ExecutionException e) { + } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) { throw new CommandLineOperationException("Failed to verify key encryption", e); + } finally { + killIfActive(verifyCommand); } } @@ -359,7 +367,7 @@ public class OpenSSLCertificateCreator { Path caKey, String caKeyPassphrase, Path csrFile - ) throws CommandLineOperationException, InterruptedException { + ) throws CommandLineOperationException { Path outFile = csrFile.resolveSibling(csrFile.getFileName().toString().replace(".csr", ".crt")); log.debug("Writing new signed certificate file {}", outFile); Path extFile = csrFile.resolveSibling(csrFile.getFileName().toString().replace(".csr", ".ext")); @@ -422,31 +430,31 @@ public class OpenSSLCertificateCreator { .redirectOutput(Slf4jStream.of(openSSLLogger).asDebug()) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - ProcessResult result = certGenProc.getFuture().get(30, TimeUnit.SECONDS); + ProcessResult result = certGenProc.getFuture().get(30, SECONDS); // Check exit code if (result.getExitValue() != 0) { - throw new CommandLineOperationException("Failed to sign certificate. Exit code: " + result.getExitValue()); + throw new CommandLineOperationException( + "Failed to sign certificate. Exit code: " + result.getExitValue() + ); } - - } catch (IOException | TimeoutException e) { + } catch (IOException | TimeoutException | ExecutionException | InterruptedException e) { throw new CommandLineOperationException("Failure running OpenSSL x509 command.", e); - } catch (ExecutionException e) { - throw new RuntimeException(e); } finally { killIfActive(certGenProc); } return outFile; } - public String getCertificateFingerprint(Path certificate) - throws CommandLineOperationException, InterruptedException { + public String getCertificateFingerprint(Path certificate) throws CommandLineOperationException { + StartedProcess fingerprintProc = null; try { - StartedProcess fingerprintProc = new ProcessExecutor() + fingerprintProc = + new ProcessExecutor() .command(resolveOpenSSL(), "x509", "-in", certificate.toString(), "-noout", "-fingerprint") .readOutput(true) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - var fingerprintResult = fingerprintProc.getFuture().get(); + var fingerprintResult = fingerprintProc.getFuture().get(30, SECONDS); String output = fingerprintResult.getOutput().getUTF8(); if (fingerprintResult.getExitValue() != 0) { @@ -473,14 +481,18 @@ public class OpenSSLCertificateCreator { ); } return "%s;%s".formatted(algorithm, fingerprint); - } catch (IOException | ExecutionException e) { + } catch (IOException | ExecutionException | TimeoutException | InterruptedException e) { throw new RuntimeException(e); + } finally { + killIfActive(fingerprintProc); } } - public CertificateRequest getCertificateInfo(Path path) throws CommandLineOperationException, InterruptedException { + public CertificateRequest getCertificateInfo(Path path) throws CommandLineOperationException { + StartedProcess infoProc = null; try { - StartedProcess infoProc = new ProcessExecutor() + infoProc = + new ProcessExecutor() .command( resolveOpenSSL(), "x509", @@ -507,7 +519,7 @@ public class OpenSSLCertificateCreator { .readOutput(true) .redirectError(Slf4jStream.of(openSSLLogger).asError()) .start(); - var infoResult = infoProc.getFuture().get(); + var infoResult = infoProc.getFuture().get(30, SECONDS); String output = infoResult.getOutput().getUTF8(); if (infoResult.getExitValue() != 0) { log.debug("Certificate info command output:\n{}", output); @@ -516,7 +528,7 @@ public class OpenSSLCertificateCreator { ); } return getCertificateInfo(output.lines().toArray(String[]::new)); - } catch (IOException | ExecutionException e) { + } catch (IOException | ExecutionException | InterruptedException | TimeoutException e) { throw new RuntimeException(e); } } diff --git a/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java b/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java index bc1265f..429efe3 100644 --- a/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java +++ b/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java @@ -32,9 +32,6 @@ public class CertificateCreationService { ) { certificate.setPrivateKey(Files.readAllBytes(certificateCreatorResult.certificateKeyPath())); certificate.setCert(Files.readAllBytes(certificateCreatorResult.certificatePath())); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException("Interrupted exception", e); } catch (CommandLineOperationException | IOException e) { throw new IllegalStateException("Failed to create certificate!", e); } @@ -81,8 +78,6 @@ public class CertificateCreationService { return certificateRepository.save(entity); } catch (CommandLineOperationException | IOException e) { throw new RuntimeException("Unable to import certificate", e); - } catch (InterruptedException e) { - throw new RuntimeException(e); } } -- 2.45.3