feat: Implement certificate fingerprinting on creation

This commit is contained in:
Magnus Leßmann (@MarkL4YG) 2024-11-18 16:38:24 +01:00
parent d709a59145
commit 1380b39977
3 changed files with 41 additions and 2 deletions

View file

@ -50,4 +50,6 @@ public class Certificate {
@Lob
private byte[] privateKey = new byte[0];
private String fingerprint;
}

View file

@ -13,6 +13,8 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -39,6 +41,10 @@ public class OpenSSLCertificateCreator {
[alt_names]
""";
private static final Pattern FINGERPRINT_EXTRACTOR = Pattern.compile(
"^(?<algo>[0-9a-z]+) Fingerprint=(?<finger>[a-z:A-Z0-9]+)",
Pattern.CASE_INSENSITIVE
);
private final ExecutableResolver executableResolver;
@ -75,13 +81,15 @@ public class OpenSSLCertificateCreator {
if (
request.getType() == RequestType.ROOT_AUTHORITY || request.getType() == RequestType.STANDALONE_CERTIFICATE
) {
return new OpenSSLCertificateResult(tmpDir, rootCert, keyFile);
String fingerprint = getCertificateFingerprint(rootCert);
return new OpenSSLCertificateResult(tmpDir, rootCert, keyFile, fingerprint);
}
Path childKey = createKeyfile(request, tmpDir.resolve("child.key"));
Path unsignedCert = createSigningRequest(request, childKey, tmpDir.resolve("child.csr"));
Path signedCert = signCertificate(request, rootCert, keyFile, unsignedCert);
return new OpenSSLCertificateResult(tmpDir, signedCert, childKey);
String fingerPrint = getCertificateFingerprint(signedCert);
return new OpenSSLCertificateResult(tmpDir, signedCert, childKey, fingerPrint);
}
private Path createKeyfile(CertificateRequest request, Path outFile)
@ -272,6 +280,34 @@ public class OpenSSLCertificateCreator {
return outFile;
}
private String getCertificateFingerprint(Path certificate)
throws CommandLineOperationException, InterruptedException {
try {
StartedProcess fingerprintProc = new ProcessExecutor()
.command(resolveOpenSSL(), "x509", "-in", certificate.toString(), "-noout", "-fingerprint")
.readOutput(true)
.redirectError(Slf4jStream.ofCaller().asError())
.start();
var fingerprintResult = fingerprintProc.getFuture().get();
String output = fingerprintResult.getOutput().getUTF8();
Matcher matcher = FINGERPRINT_EXTRACTOR.matcher(output);
if (!matcher.find()) {
log.debug(output);
throw new CommandLineOperationException("Unexpected output of fingerprint command. (See log for more details)");
}
String algorithm = matcher.group("algo");
String fingerprint = matcher.group("finger");
if (StringUtils.isBlank(algorithm) || StringUtils.isBlank(fingerprint)) {
throw new CommandLineOperationException(
"Unexpected output of fingerprint command: %s %s".formatted(algorithm, fingerprint)
);
}
return "%s;%s".formatted(algorithm, fingerprint);
} catch (IOException | ExecutionException e) {
throw new RuntimeException(e);
}
}
private String resolveOpenSSL() throws CommandLineOperationException {
try {
return executableResolver.getOpenSSLPath();

View file

@ -18,6 +18,7 @@ public class OpenSSLCertificateResult implements AutoCloseable {
private final Path tmpDir;
private final Path certificatePath;
private final Path privateKeyPath;
private final String certificateFingerPrint;
@Override
public void close() throws IOException {