From 98a6556bf9b6abca18a23b1a6c052edbae9c6e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sun, 17 Nov 2024 16:51:11 +0100 Subject: [PATCH] :construction: Create method to generate x509 certs --- .../openssl/CertificateRequest.java | 10 +++ .../openssl/CertificateSubject.java | 16 +++++ .../openssl/OpenSSLCertificateCreator.java | 63 ++++++++++++++++++- 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/mlessmann/certassist/openssl/CertificateSubject.java diff --git a/src/main/java/de/mlessmann/certassist/openssl/CertificateRequest.java b/src/main/java/de/mlessmann/certassist/openssl/CertificateRequest.java index 126944c..6e9ab73 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/CertificateRequest.java +++ b/src/main/java/de/mlessmann/certassist/openssl/CertificateRequest.java @@ -32,6 +32,16 @@ public class CertificateRequest { @Builder.Default private int requestedKeyLength = 4096; + @Getter + @Setter + @Builder.Default + private int requestedValidityDays = 365; + + @Getter + @Setter + @Builder.Default + private CertificateSubject subject = CertificateSubject.builder().build(); + public enum RequestType { ROOT_AUTHORITY, STANDALONE_CERTIFICATE, diff --git a/src/main/java/de/mlessmann/certassist/openssl/CertificateSubject.java b/src/main/java/de/mlessmann/certassist/openssl/CertificateSubject.java new file mode 100644 index 0000000..5884d4a --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/openssl/CertificateSubject.java @@ -0,0 +1,16 @@ +package de.mlessmann.certassist.openssl; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class CertificateSubject { + + private String emailAddress; + private String organization; + private String organizationalUnit; + private String country; + private String state; + private String locality; +} diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 42d22fc..49d0466 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -3,6 +3,7 @@ package de.mlessmann.certassist.openssl; import de.mlessmann.certassist.ExecutableResolver; import de.mlessmann.certassist.except.CommandLineOperationException; import de.mlessmann.certassist.except.UnresolvableCLIDependency; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.Nullable; @@ -21,8 +22,8 @@ import static org.slf4j.LoggerFactory.getLogger; @Service public class OpenSSLCertificateCreator { + public static final String OPENSSL_CERT_SUBJECT_TEMPLATE = "/C=ISO-COUNTRY/ST=STATE/L=LOCALITY/O=ORGANIZATION/CN=COMMON-NAME"; private static final Logger LOGGER = getLogger(OpenSSLCertificateCreator.class); - private final ExecutableResolver executableResolver; @Autowired @@ -30,6 +31,31 @@ public class OpenSSLCertificateCreator { this.executableResolver = executableResolver; } + private static String buildSubjectArg(CertificateRequest request) { + String certSubject = OPENSSL_CERT_SUBJECT_TEMPLATE.replace("ISO-COUNTRY", request.getSubject() + .getCountry()) + .replace("STATE", request.getSubject() + .getState()) + .replace("LOCALITY", request.getSubject() + .getLocality()) + .replace("ORGANIZATION", request.getSubject() + .getOrganization()) + .replace("COMMON-NAME", request.getCommonName()); + + if (StringUtils.isNotBlank(request.getSubject() + .getOrganizationalUnit())) { + certSubject += "/OU=" + request.getSubject() + .getOrganizationalUnit(); + } + + if (StringUtils.isNotBlank(request.getSubject() + .getEmailAddress())) { + certSubject += "/emailAddress=" + request.getSubject() + .getEmailAddress(); + } + return certSubject; + } + @Nullable public OpenSSLCertificateResult createCertificate(CertificateRequest request) throws CommandLineOperationException, InterruptedException { Path tmpDir; @@ -40,6 +66,7 @@ public class OpenSSLCertificateCreator { } createKeyfile(request, tmpDir); + createCertificate(request, tmpDir); return new OpenSSLCertificateResult(tmpDir); } @@ -70,6 +97,40 @@ public class OpenSSLCertificateCreator { return keyFile; } + private Path createCertificate(CertificateRequest request, Path tmpDir) throws CommandLineOperationException, InterruptedException { + Path keyFile = tmpDir.resolve("root.key") + .toAbsolutePath(); + Path certFile = tmpDir.resolve("root.crt") + .toAbsolutePath(); + LOGGER.atDebug() + .log("Writing new certificate file {}", certFile); + + String certSubject = buildSubjectArg(request); + try { + StartedProcess keygenProc = new ProcessExecutor().command(resolveOpenSSL(), "req", "x509", "-new", "-nodes", + "-key", keyFile.toString(), "-sha256", "-days", + Integer.toString( + request.getRequestedValidityDays()), + "-out", + certFile.toString(), + "-passout", "env:KEY_PASS", "-utf8", "-subj", + certSubject) + .environment("KEY_PASS", request.getOid()) + .redirectOutput(Slf4jStream.ofCaller() + .asDebug()) + .redirectError(Slf4jStream.ofCaller() + .asError()) + .start(); + keygenProc.getFuture() + .get(); + } catch (IOException e) { + throw new CommandLineOperationException("Failure running OpenSSL req command.", e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + return certFile; + } + private String resolveOpenSSL() throws CommandLineOperationException { try { return executableResolver.getOpenSSLPath();