diff --git a/src/main/java/de/mlessmann/certassist/models/Certificate.java b/src/main/java/de/mlessmann/certassist/models/Certificate.java index 93d9016..ff50266 100644 --- a/src/main/java/de/mlessmann/certassist/models/Certificate.java +++ b/src/main/java/de/mlessmann/certassist/models/Certificate.java @@ -50,4 +50,6 @@ public class Certificate { @Lob private byte[] privateKey = new byte[0]; + + private String fingerprint; } diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index 224d3f3..36d0bf1 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -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( + "^(?[0-9a-z]+) Fingerprint=(?[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(); diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java index 2230765..3c72222 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateResult.java @@ -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 {