From 8f198f944ea4fad4a6b0db777300c31f2e72a3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sun, 10 Nov 2024 19:08:19 +0100 Subject: [PATCH] :construction: Work on allowing the creation of OpenSSL certificates --- .../certassist/ExecutableResolver.java | 122 ++++++++++-------- .../certassist/OpenSSLCertificateCreator.java | 39 ------ .../{ => openssl}/CertificateRequest.java | 75 ++++++----- .../openssl/OpenSSLCertificateCreator.java | 62 +++++++++ .../TestOpenSSLCertificateCreator.java | 56 ++++---- 5 files changed, 197 insertions(+), 157 deletions(-) delete mode 100644 src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java rename src/main/java/de/mlessmann/certassist/{ => openssl}/CertificateRequest.java (80%) create mode 100644 src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java diff --git a/src/main/java/de/mlessmann/certassist/ExecutableResolver.java b/src/main/java/de/mlessmann/certassist/ExecutableResolver.java index abf54a2..e2a10b6 100644 --- a/src/main/java/de/mlessmann/certassist/ExecutableResolver.java +++ b/src/main/java/de/mlessmann/certassist/ExecutableResolver.java @@ -1,55 +1,67 @@ -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(); - } -} +package de.mlessmann.certassist; + +import de.mlessmann.certassist.except.UnresolvableCLIDependency; +import lombok.Setter; +import org.apache.commons.lang3.SystemUtils; +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.List; +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) { + for (String fileExtension : getAllowedExtensions()) { + Path executablePath = Path.of(pathEntry, executableName + fileExtension); + 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(); + } + + public List getAllowedExtensions() { + if (SystemUtils.IS_OS_WINDOWS) { + return List.of(".exe", ".bat", ".cmd"); + } else { + return List.of("", ".sh"); + } + } +} diff --git a/src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java deleted file mode 100644 index e410745..0000000 --- a/src/main/java/de/mlessmann/certassist/OpenSSLCertificateCreator.java +++ /dev/null @@ -1,39 +0,0 @@ -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/CertificateRequest.java b/src/main/java/de/mlessmann/certassist/openssl/CertificateRequest.java similarity index 80% rename from src/main/java/de/mlessmann/certassist/CertificateRequest.java rename to src/main/java/de/mlessmann/certassist/openssl/CertificateRequest.java index 12e6599..126944c 100644 --- a/src/main/java/de/mlessmann/certassist/CertificateRequest.java +++ b/src/main/java/de/mlessmann/certassist/openssl/CertificateRequest.java @@ -1,35 +1,40 @@ -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 - } -} +package de.mlessmann.certassist.openssl; + +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; + + @Getter + @Setter + @Builder.Default + private int requestedKeyLength = 4096; + + public enum RequestType { + ROOT_AUTHORITY, + STANDALONE_CERTIFICATE, + NORMAL_CERTIFICATE + } +} diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java new file mode 100644 index 0000000..adb7671 --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -0,0 +1,62 @@ +package de.mlessmann.certassist.openssl; + +import de.mlessmann.certassist.ExecutableResolver; +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 java.nio.file.Files; +import java.nio.file.Path; + +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) { + Path tmpDir; + try { + tmpDir = Files.createTempDirectory("certassist"); + } catch (IOException e) { + LOGGER.atError() + .log("Could not create temp directory for openssl generator!", e); + return; + } + + try { + createKeyfile(request, tmpDir); + + } catch (IOException | InterruptedException e) { + LOGGER.atError() + .log(e.getMessage()); + } catch (UnresolvableCLIDependency e) { + LOGGER.atError() + .log(e.getMessage()); + } + } + + private Path createKeyfile(CertificateRequest request, Path tmpDir) throws UnresolvableCLIDependency, IOException, InterruptedException { + Path keyFile = tmpDir.resolve("root.key").toAbsolutePath(); + LOGGER.atDebug().log("Creating root certificate key at: {}", keyFile); + + String openSSLPath = executableResolver.getOpenSSLPath(); + Process createRootKeyProc = new ProcessBuilder() + .command(openSSLPath, "req", "genrsa", "-des3", "-out", keyFile.toString(), + Integer.toString(request.getRequestedKeyLength())) + .inheritIO() + .start(); + createRootKeyProc.waitFor(); + return keyFile; + } +} diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java index 66a21b2..80dc058 100644 --- a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java @@ -1,28 +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); - } -} +package de.mlessmann.certassist; + +import de.mlessmann.certassist.openssl.CertificateRequest; +import de.mlessmann.certassist.openssl.CertificateRequest.RequestType; +import de.mlessmann.certassist.openssl.OpenSSLCertificateCreator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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); + } +}