diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 7f12ce7..9159936 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -11,12 +11,12 @@ import java.nio.file.StandardOpenOption; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.lang.Nullable; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.StartedProcess; @@ -59,7 +59,7 @@ public class OpenSSLCertificateCreator { return certSubject; } - @Nullable + @NonNull public OpenSSLCertificateResult createCertificate(CertificateRequest request) throws CommandLineOperationException, InterruptedException { Path tmpDir; @@ -73,9 +73,9 @@ public class OpenSSLCertificateCreator { Path rootCert = createCertificate(request, keyFile, tmpDir.resolve("root.crt")); Path childKey = createKeyfile(request, tmpDir.resolve("child.key")); - Path unsignedCert = createCertificate(request, childKey, tmpDir.resolve("child.csr")); + Path unsignedCert = createSigningRequest(request, childKey, tmpDir.resolve("child.csr")); Path signedCert = signCertificate(request, rootCert, keyFile, unsignedCert); - return new OpenSSLCertificateResult(tmpDir); + return new OpenSSLCertificateResult(tmpDir, signedCert, childKey); } private Path createKeyfile(CertificateRequest request, Path outFile) @@ -117,6 +117,7 @@ public class OpenSSLCertificateCreator { .command( resolveOpenSSL(), "req", + "-x509", "-new", "-passin", "env:KEY_PASS", @@ -146,6 +147,57 @@ public class OpenSSLCertificateCreator { return outFile; } + private Path createSigningRequest(CertificateRequest request, Path keyFile, Path outFile) + throws CommandLineOperationException, InterruptedException { + log.atDebug().log("Writing new certificate signing request file {}", outFile); + + String certSubject = buildSubjectArg(request); + try { + StartedProcess certGenProc = new ProcessExecutor() + .command( + resolveOpenSSL(), + "req", + "-new", + "-passin", + "env:KEY_PASS", + "-key", + keyFile.toString(), + "-sha256", + "-out", + outFile.toString(), + "-passout", + "env:KEY_PASS", + "-utf8", + "-subj", + certSubject + ) + .environment("KEY_PASS", request.getOid()) + .redirectOutput(Slf4jStream.ofCaller().asDebug()) + .redirectError(Slf4jStream.ofCaller().asError()) + .start(); + certGenProc.getFuture().get(); + } catch (IOException e) { + throw new CommandLineOperationException("Failure running OpenSSL req command.", e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + return outFile; + } + + public boolean verifyCertificate(Path certFile) throws CommandLineOperationException { + try { + StartedProcess verifyCommand = new ProcessExecutor() + .command(resolveOpenSSL(), "x509", "-in", certFile.toString(), "-text", "-noout") + .redirectOutput(Slf4jStream.ofCaller().asDebug()) + .redirectError(Slf4jStream.ofCaller().asError()) + .start(); + var verifyResult = verifyCommand.getFuture().get(); + return verifyResult.getExitValue() == 0; + } catch (IOException | InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + private Path signCertificate(CertificateRequest request, Path caCert, Path caKey, Path csrFile) throws CommandLineOperationException, InterruptedException { Path outFile = csrFile.resolveSibling(csrFile.getFileName().toString().replace(".csr", ".crt")); diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java index 7c53933..2230765 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java @@ -6,14 +6,18 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; import lombok.AccessLevel; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@Getter public class OpenSSLCertificateResult implements AutoCloseable { private final Path tmpDir; + private final Path certificatePath; + private final Path privateKeyPath; @Override public void close() throws IOException { diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java index 7480f44..e3be9e6 100644 --- a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java @@ -1,5 +1,7 @@ package de.mlessmann.certassist; +import static org.assertj.core.api.Assertions.*; + import de.mlessmann.certassist.openssl.CertificateRequest; import de.mlessmann.certassist.openssl.CertificateRequest.RequestType; import de.mlessmann.certassist.openssl.CertificateRequestExtension; @@ -29,6 +31,7 @@ class TestOpenSSLCertificateCreator { .build(); try (var cert = openSSLCertificateCreator.createCertificate(certRequest)) { + assertThat(openSSLCertificateCreator.verifyCertificate(cert.getCertificatePath())).isEqualTo(true); System.out.println("Certificate created: " + cert); } }