- Delete temp directory using FileTree visitor recursively - Update CertificateRequestBuilder to accept subject info directly from builder
141 lines
5.8 KiB
Java
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);
|
|
}
|
|
}
|
|
}
|