package de.mlessmann.certassist.service; import static java.util.Objects.requireNonNull; import de.mlessmann.certassist.Constants; import de.mlessmann.certassist.DeleteRecursiveFileVisitor; import de.mlessmann.certassist.models.Certificate; import de.mlessmann.certassist.models.CertificateType; import de.mlessmann.certassist.openssl.CertificateProvider; import de.mlessmann.certassist.openssl.CertificateUsage; import de.mlessmann.certassist.repositories.CertificateRepository; import java.io.IOException; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @Slf4j @RequiredArgsConstructor public class CertificateProviderImpl implements CertificateProvider { private static final OpenOption[] CREATE_TRUNCATE = new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, }; private final CertificateRepository certificateRepository; @Override public CertificateUsage requestCertificateUsage(String fingerprint) { requireNonNull(fingerprint, "Fingerprint must be provided."); Certificate certFromDB; try { certFromDB = certificateRepository.findByFingerprintIs(fingerprint); } catch (RuntimeException e) { log.error("Failed to retrieve certificate from database by fingerprint: {}", fingerprint); throw e; } if (certFromDB == null) { throw new IllegalArgumentException("Unknown fingerprint"); } boolean selfSigned = certFromDB.getType() == CertificateType.ROOT_CA || certFromDB.getType() == CertificateType.STANDALONE_CERT; try { Path tempDirectory = Files.createTempDirectory(Constants.CERTASSIST_TMP_PREFIX); Files.write(tempDirectory.resolve("key.pem"), certFromDB.getPrivateKey(), CREATE_TRUNCATE); Files.write(tempDirectory.resolve("cert.pem"), certFromDB.getCert(), CREATE_TRUNCATE); if (!selfSigned) { Files.write(tempDirectory.resolve("fullchain.pem"), certFromDB.getFullchain(), CREATE_TRUNCATE); } return new ExtractedCert(tempDirectory, certFromDB.getFingerprint()); } catch (IOException e) { // TODO: Better exception definitions throw new RuntimeException("Unable to temporarily store certificate for use.", e); } } @Slf4j private record ExtractedCert(Path tempDir, String fingerprint) implements CertificateUsage { @Override public Path certificateKeyPath() { return this.tempDir.resolve("key.pem"); } @Override public Path certificatePath() { return this.tempDir.resolve("cert.pem"); } @Override public Path fullchainPath() { Path fcFile = this.tempDir.resolve("fullchain.pem"); if (Files.exists(fcFile)) { return fcFile; } return null; } @Override public void close() { try { Files.walkFileTree(this.tempDir, new DeleteRecursiveFileVisitor()); Files.deleteIfExists(this.tempDir); } catch (IOException e) { log.error("Unable to clean up temporary directory: {}", this.tempDir, e); } } } }