home-cert-assistant/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java
Magnus Leßmann (@MarkL4YG) b5571aa2e5 🚧 Fix issue where cert cleanup fails
- Delete temp directory using FileTree visitor recursively
- Update CertificateRequestBuilder to accept subject info directly from
  builder
2024-11-17 20:52:16 +01:00

141 lines
5.8 KiB
Java

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;
import org.springframework.stereotype.Service;
import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.StartedProcess;
import org.zeroturnaround.exec.stream.slf4j.Slf4jStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
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
public OpenSSLCertificateCreator(ExecutableResolver executableResolver) {
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;
try {
tmpDir = Files.createTempDirectory("certassist");
} catch (IOException e) {
throw new CommandLineOperationException("Could not create temporary directory for certificate creation", e);
}
createKeyfile(request, tmpDir);
createCertificate(request, tmpDir);
return new OpenSSLCertificateResult(tmpDir);
}
private Path createKeyfile(CertificateRequest request, Path tmpDir) throws CommandLineOperationException, InterruptedException {
Path keyFile = tmpDir.resolve("root.key")
.toAbsolutePath();
LOGGER.atDebug()
.log("Writing new certificate key to {}", keyFile);
try {
StartedProcess keygenProc = new ProcessExecutor().command(resolveOpenSSL(), "genrsa", "-out",
keyFile.toString(),
"-passout", "env:KEY_PASS",
Integer.toString(request.getRequestedKeyLength()))
.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 keygen command.", e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
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", "-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();
} catch (UnresolvableCLIDependency e) {
throw new CommandLineOperationException(e);
}
}
}