From c9fef111ad7ec4f2c8fd00160f9137450c8c5a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Mon, 4 Nov 2024 21:53:12 +0100 Subject: [PATCH] :construction: Start work on executing openssl commands --- .../certassist/CertificateRequest.java | 35 ++++++++++++ .../certassist/ExecutableResolver.java | 55 +++++++++++++++++++ .../certassist/OpenSSLCertificateCreator.java | 39 +++++++++++++ .../except/UnresolvableCLIDependency.java | 17 ++++++ .../TestOpenSSLCertificateCreator.java | 28 ++++++++++ 5 files changed, 174 insertions(+) create mode 100644 src/main/java/de/mlessmann/certassist/CertificateRequest.java create mode 100644 src/main/java/de/mlessmann/certassist/ExecutableResolver.java create mode 100644 src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java create mode 100644 src/main/java/de/mlessmann/certassist/except/UnresolvableCLIDependency.java create mode 100644 src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java diff --git a/src/main/java/de/mlessmann/certassist/CertificateRequest.java b/src/main/java/de/mlessmann/certassist/CertificateRequest.java new file mode 100644 index 0000000..12e6599 --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/CertificateRequest.java @@ -0,0 +1,35 @@ +package de.mlessmann.certassist; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.UUID; + + +@Builder +public class CertificateRequest { + + @Getter + @Setter + @Builder.Default + private String oid = UUID.randomUUID().toString(); + + @Getter + @Setter + private RequestType type; + + @Getter + @Setter + private String commonName; + + @Getter + @Setter + private String trustingAuthority; + + public enum RequestType { + ROOT_AUTHORITY, + STANDALONE_CERTIFICATE, + NORMAL_CERTIFICATE + } +} diff --git a/src/main/java/de/mlessmann/certassist/ExecutableResolver.java b/src/main/java/de/mlessmann/certassist/ExecutableResolver.java new file mode 100644 index 0000000..abf54a2 --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/ExecutableResolver.java @@ -0,0 +1,55 @@ +package de.mlessmann.certassist; + +import de.mlessmann.certassist.except.UnresolvableCLIDependency; +import lombok.Setter; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; + +import static org.slf4j.LoggerFactory.getLogger; + +@Service +public class ExecutableResolver { + + private static final Logger LOGGER = getLogger(ExecutableResolver.class); + + @Value("${openssl.path:#{null}}") + private String opensslPath; + + public String getOpenSSLPath() throws UnresolvableCLIDependency { + if (opensslPath == null) { + LOGGER.atDebug().log("No openssl path configured, falling back to resolving by shell."); + var optSSLPath = searchCommandFromPath("openssl"); + opensslPath = optSSLPath.orElseThrow(() -> new UnresolvableCLIDependency("openssl", "openssl.path")); + } + + Path configuredPath = new File(opensslPath).toPath(); + if (!Files.isRegularFile(configuredPath)) { + throw new UnresolvableCLIDependency("openssl", "openssl.path"); + } + + return opensslPath; + } + + private Optional searchCommandFromPath(String executableName) { + String envPath = System.getenv("PATH"); + Objects.requireNonNull(envPath, "Environment variable 'PATH' is not set?!"); + String[] pathEntries = envPath.split(File.pathSeparator); + + for (String pathEntry : pathEntries) { + Path executablePath = Path.of(pathEntry, executableName); + if (Files.isRegularFile(executablePath) && Files.isExecutable(executablePath)) { + return Optional.of(executablePath.toString()); + } + } + + LOGGER.error("Could not find executable '{}' in PATH. Make sure that it exists on the of the directory and is executable.", executableName); + return Optional.empty(); + } +} diff --git a/src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java new file mode 100644 index 0000000..e410745 --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java @@ -0,0 +1,39 @@ +package de.mlessmann.certassist; + +import de.mlessmann.certassist.except.UnresolvableCLIDependency; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +import static org.slf4j.LoggerFactory.getLogger; + +@Service +public class OpenSSLCertificateCreator { + + private static final Logger LOGGER = getLogger(OpenSSLCertificateCreator.class); + + private final ExecutableResolver executableResolver; + + @Autowired + public OpenSSLCertificateCreator(ExecutableResolver executableResolver) { + this.executableResolver = executableResolver; + } + + public void createCertificate(CertificateRequest request) { + try { + String openSSLPath = executableResolver.getOpenSSLPath(); + + Process process = new ProcessBuilder() + .command(openSSLPath, "--version") + .redirectOutput(ProcessBuilder.Redirect.PIPE) + .start(); + process.waitFor(); + } catch (IOException | InterruptedException e) { + LOGGER.atError().log(e.getMessage()); + } catch (UnresolvableCLIDependency e) { + LOGGER.atError().log(e.getMessage()); + } + } +} diff --git a/src/main/java/de/mlessmann/certassist/except/UnresolvableCLIDependency.java b/src/main/java/de/mlessmann/certassist/except/UnresolvableCLIDependency.java new file mode 100644 index 0000000..cbaab23 --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/except/UnresolvableCLIDependency.java @@ -0,0 +1,17 @@ +package de.mlessmann.certassist.except; + +import lombok.Getter; + +public class UnresolvableCLIDependency extends Exception { + + @Getter + private final String executableName; + @Getter + private final String propertyName; + + public UnresolvableCLIDependency(String executableName, String propertyName) { + super("Could not resolve executable for '%s'. (Use property '%s' to point the application directly to the executable.)".formatted(executableName, propertyName)); + this.executableName = executableName; + this.propertyName = propertyName; + } +} diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java new file mode 100644 index 0000000..66a21b2 --- /dev/null +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java @@ -0,0 +1,28 @@ +package de.mlessmann.certassist; + +import de.mlessmann.certassist.CertificateRequest.RequestType; +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; + +public class TestOpenSSLCertificateCreator { + + private OpenSSLCertificateCreator openSSLCertificateCreator; + + @BeforeEach + void setUp() { + ExecutableResolver executableResolver = new ExecutableResolver(); + openSSLCertificateCreator = new OpenSSLCertificateCreator(executableResolver); + } + + @Test + void testCertificateCreation() { + CertificateRequest certRequest = CertificateRequest.builder() + .commonName("test.home") + .type(RequestType.STANDALONE_CERTIFICATE) + .build(); + + openSSLCertificateCreator.createCertificate(certRequest); + } +}