chore: Refactor how OpenSSL processes are started
This commit is contained in:
parent
97eea3a20f
commit
8ef6234bc5
1 changed files with 44 additions and 21 deletions
|
@ -18,6 +18,8 @@ import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
@ -54,6 +56,8 @@ public class OpenSSLCertificateCreator {
|
||||||
private static final Pattern FINGERPRINT_EXTRACTOR = Pattern.compile(
|
private static final Pattern FINGERPRINT_EXTRACTOR = Pattern.compile(
|
||||||
"^(?<algo>[0-9a-zA-Z]+) (?i)Fingerprint(?-i)=(?<finger>[a-z:A-Z0-9]+)"
|
"^(?<algo>[0-9a-zA-Z]+) (?i)Fingerprint(?-i)=(?<finger>[a-z:A-Z0-9]+)"
|
||||||
);
|
);
|
||||||
|
private static final String OSSL_ENV_KEY_PW = "KEY_PASS";
|
||||||
|
private static final String OSSL_ARG_KEY_PW = "env:" + OSSL_ENV_KEY_PW;
|
||||||
private final AtomicBoolean versionLogged = new AtomicBoolean(false);
|
private final AtomicBoolean versionLogged = new AtomicBoolean(false);
|
||||||
|
|
||||||
private final ExecutableResolver executableResolver;
|
private final ExecutableResolver executableResolver;
|
||||||
|
@ -78,6 +82,25 @@ public class OpenSSLCertificateCreator {
|
||||||
return certSubject;
|
return certSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void killIfActive(StartedProcess process) {
|
||||||
|
if (process == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Process sysProc = process.getProcess();
|
||||||
|
if (!sysProc.isAlive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.debug("Process is still alive. Asking politely for it to destroy itself and waiting on exit for 2s");
|
||||||
|
sysProc.destroy();
|
||||||
|
sysProc.waitFor(2_000, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.debug("Interrupted while waiting for process to terminate. Registering forceful termination onExit", e);
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(sysProc::destroyForcibly));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public OpenSSLCertificateResult createCertificate(CertificateRequest request)
|
public OpenSSLCertificateResult createCertificate(CertificateRequest request)
|
||||||
throws CommandLineOperationException, InterruptedException {
|
throws CommandLineOperationException, InterruptedException {
|
||||||
|
@ -88,28 +111,28 @@ public class OpenSSLCertificateCreator {
|
||||||
throw new CommandLineOperationException("Could not create temporary directory for certificate creation", e);
|
throw new CommandLineOperationException("Could not create temporary directory for certificate creation", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
String keypassphrase = passwordProvider.generateNewPassword();
|
String keyPassphrase = passwordProvider.generateNewPassword();
|
||||||
Path keyFile = createKeyfile(request, tmpDir.resolve("certificate.key"), keypassphrase);
|
Path keyFile = createKeyfile(request, tmpDir.resolve("certificate.key"), keyPassphrase);
|
||||||
if (
|
if (
|
||||||
request.getType() == RequestType.ROOT_AUTHORITY || request.getType() == RequestType.STANDALONE_CERTIFICATE
|
request.getType() == RequestType.ROOT_AUTHORITY || request.getType() == RequestType.STANDALONE_CERTIFICATE
|
||||||
) {
|
) {
|
||||||
Path certificate = createCertificate(request, keyFile, tmpDir.resolve("certificate.crt"), keypassphrase);
|
Path certificate = createCertificate(request, keyFile, tmpDir.resolve("certificate.crt"), keyPassphrase);
|
||||||
String fingerprint = getCertificateFingerprint(certificate);
|
String fingerprint = getCertificateFingerprint(certificate);
|
||||||
passwordProvider.setPasswordFor(fingerprint, keypassphrase);
|
passwordProvider.setPasswordFor(fingerprint, keyPassphrase);
|
||||||
return new OpenSSLCertificateResult(tmpDir, certificate, keyFile, certificate, fingerprint);
|
return new OpenSSLCertificateResult(tmpDir, certificate, keyFile, certificate, fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var certAuthority = certificateProvider.requestCertificateUsage(request.getTrustingAuthority())) {
|
try (var certAuthority = certificateProvider.requestCertificateUsage(request.getTrustingAuthority())) {
|
||||||
Path unsignedCert = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), keypassphrase);
|
Path unsignedCert = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), keyPassphrase);
|
||||||
Path signedCert = signCertificate(
|
Path signedCert = signCertificate(
|
||||||
request,
|
request,
|
||||||
certAuthority.certificatePath(),
|
certAuthority.certificatePath(),
|
||||||
certAuthority.certificateKeyPath(),
|
certAuthority.certificateKeyPath(),
|
||||||
unsignedCert,
|
unsignedCert,
|
||||||
keypassphrase
|
keyPassphrase
|
||||||
);
|
);
|
||||||
String fingerprint = getCertificateFingerprint(signedCert);
|
String fingerprint = getCertificateFingerprint(signedCert);
|
||||||
passwordProvider.setPasswordFor(fingerprint, keypassphrase);
|
passwordProvider.setPasswordFor(fingerprint, keyPassphrase);
|
||||||
|
|
||||||
Path fullchain = tmpDir.resolve("fullchain.pem");
|
Path fullchain = tmpDir.resolve("fullchain.pem");
|
||||||
try {
|
try {
|
||||||
|
@ -140,10 +163,10 @@ public class OpenSSLCertificateCreator {
|
||||||
keyFile.toString(),
|
keyFile.toString(),
|
||||||
"-aes256",
|
"-aes256",
|
||||||
"-passout",
|
"-passout",
|
||||||
"env:KEY_PASS",
|
OSSL_ARG_KEY_PW,
|
||||||
Integer.toString(request.getRequestedKeyLength())
|
Integer.toString(request.getRequestedKeyLength())
|
||||||
)
|
)
|
||||||
.environment("KEY_PASS", filePassword)
|
.environment(OSSL_ENV_KEY_PW, filePassword)
|
||||||
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
||||||
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
||||||
.start();
|
.start();
|
||||||
|
@ -156,20 +179,22 @@ public class OpenSSLCertificateCreator {
|
||||||
return keyFile;
|
return keyFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path createCertificate(CertificateRequest request, Path keyFile, Path outFile, String certPassword)
|
private Path createCertificate(CertificateRequest request, Path keyFile, Path outFile, String keyPassphrase)
|
||||||
throws CommandLineOperationException, InterruptedException {
|
throws CommandLineOperationException, InterruptedException {
|
||||||
log.debug("Writing new certificate file {}", outFile);
|
log.debug("Writing new certificate file {}", outFile);
|
||||||
|
|
||||||
String certSubject = buildSubjectArg(request);
|
String certSubject = buildSubjectArg(request);
|
||||||
|
StartedProcess certGenProc = null;
|
||||||
try {
|
try {
|
||||||
StartedProcess certGenProc = new ProcessExecutor()
|
certGenProc =
|
||||||
|
new ProcessExecutor()
|
||||||
.command(
|
.command(
|
||||||
resolveOpenSSL(),
|
resolveOpenSSL(),
|
||||||
"req",
|
"req",
|
||||||
"-x509",
|
"-x509",
|
||||||
"-new",
|
"-new",
|
||||||
"-passin",
|
"-passin",
|
||||||
"env:KEY_PASS",
|
OSSL_ARG_KEY_PW,
|
||||||
"-key",
|
"-key",
|
||||||
keyFile.toString(),
|
keyFile.toString(),
|
||||||
"-sha256",
|
"-sha256",
|
||||||
|
@ -177,13 +202,11 @@ public class OpenSSLCertificateCreator {
|
||||||
Integer.toString(request.getRequestedValidityDays()),
|
Integer.toString(request.getRequestedValidityDays()),
|
||||||
"-out",
|
"-out",
|
||||||
outFile.toString(),
|
outFile.toString(),
|
||||||
"-passout",
|
|
||||||
"env:KEY_PASS",
|
|
||||||
"-utf8",
|
"-utf8",
|
||||||
"-subj",
|
"-subj",
|
||||||
certSubject
|
certSubject
|
||||||
)
|
)
|
||||||
.environment("KEY_PASS", certPassword)
|
.environment(OSSL_ENV_KEY_PW, keyPassphrase)
|
||||||
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
||||||
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
||||||
.start();
|
.start();
|
||||||
|
@ -192,6 +215,8 @@ public class OpenSSLCertificateCreator {
|
||||||
throw new CommandLineOperationException("Failure running OpenSSL req command.", e);
|
throw new CommandLineOperationException("Failure running OpenSSL req command.", e);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
killIfActive(certGenProc);
|
||||||
}
|
}
|
||||||
return outFile;
|
return outFile;
|
||||||
}
|
}
|
||||||
|
@ -208,19 +233,17 @@ public class OpenSSLCertificateCreator {
|
||||||
"req",
|
"req",
|
||||||
"-new",
|
"-new",
|
||||||
"-passin",
|
"-passin",
|
||||||
"env:KEY_PASS",
|
OSSL_ARG_KEY_PW,
|
||||||
"-key",
|
"-key",
|
||||||
keyFile.toString(),
|
keyFile.toString(),
|
||||||
"-sha256",
|
"-sha256",
|
||||||
"-out",
|
"-out",
|
||||||
outFile.toString(),
|
outFile.toString(),
|
||||||
"-passout",
|
|
||||||
"env:KEY_PASS",
|
|
||||||
"-utf8",
|
"-utf8",
|
||||||
"-subj",
|
"-subj",
|
||||||
certSubject
|
certSubject
|
||||||
)
|
)
|
||||||
.environment("KEY_PASS", certPassword)
|
.environment(OSSL_ENV_KEY_PW, certPassword)
|
||||||
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
||||||
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
||||||
.start();
|
.start();
|
||||||
|
@ -393,8 +416,8 @@ public class OpenSSLCertificateCreator {
|
||||||
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
.redirectOutput(Slf4jStream.of(openSSLLogger).asDebug())
|
||||||
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
.redirectError(Slf4jStream.of(openSSLLogger).asError())
|
||||||
.start();
|
.start();
|
||||||
certGenProc.getFuture().get();
|
certGenProc.getFuture().get(30, TimeUnit.SECONDS);
|
||||||
} catch (IOException e) {
|
} catch (IOException | TimeoutException e) {
|
||||||
throw new CommandLineOperationException("Failure running OpenSSL x509 command.", e);
|
throw new CommandLineOperationException("Failure running OpenSSL x509 command.", e);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
Loading…
Add table
Reference in a new issue