From fc34320ffd7297b302f345bc3c22980d2ddd1dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Tue, 19 Nov 2024 23:02:27 +0100 Subject: [PATCH] feat: Implement way of retrieving CAs in signing process --- .../openssl/CertificateProvider.java | 11 +++++++++ .../certassist/openssl/CertificateUsage.java | 17 ++++++++++++++ .../openssl/OpenSSLCertificateCreator.java | 23 +++++++++++-------- .../TestOpenSSLCertificateCreator.java | 19 +++++++++------ 4 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 src/main/java/de/mlessmann/certassist/openssl/CertificateProvider.java create mode 100644 src/main/java/de/mlessmann/certassist/openssl/CertificateUsage.java diff --git a/src/main/java/de/mlessmann/certassist/openssl/CertificateProvider.java b/src/main/java/de/mlessmann/certassist/openssl/CertificateProvider.java new file mode 100644 index 0000000..23df5ae --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/openssl/CertificateProvider.java @@ -0,0 +1,11 @@ +package de.mlessmann.certassist.openssl; + +/** + * Accessor interface to the certificate storage. + */ +public interface CertificateProvider { + /** + * A stored certificate is needed (e.g. for command line operations when signing sub certificates). + */ + CertificateUsage requestCertificateUsage(String fingerprint); +} diff --git a/src/main/java/de/mlessmann/certassist/openssl/CertificateUsage.java b/src/main/java/de/mlessmann/certassist/openssl/CertificateUsage.java new file mode 100644 index 0000000..737fd86 --- /dev/null +++ b/src/main/java/de/mlessmann/certassist/openssl/CertificateUsage.java @@ -0,0 +1,17 @@ +package de.mlessmann.certassist.openssl; + +import java.nio.file.Path; + +/** + * Instance of a certificate that is temporarily stored on disk to be available for use in command line calls. + * The instance implements AutoCloseable to enable cleanup after the stored files are no longer needed. + * @implSpec The files should be removed from disk when the instance is closed, UNLESS the provided paths are the permanent storage location for the certificate files. + */ +public interface CertificateUsage extends AutoCloseable { + Path certificatePath(); + Path certificateKeyPath(); + String fingerprint(); + + @Override + void close(); +} diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index bc9f7be..cdc9d58 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -47,6 +47,7 @@ public class OpenSSLCertificateCreator { private final ExecutableResolver executableResolver; private final CertPasswordProvider passwordProvider; + private final CertificateProvider certificateProvider; private static String buildSubjectArg(CertificateRequest request) { String certSubject = OPENSSL_CERT_SUBJECT_TEMPLATE @@ -77,21 +78,23 @@ public class OpenSSLCertificateCreator { } String certPassword = passwordProvider.generateNewPassword(); - Path keyFile = createKeyfile(request, tmpDir.resolve("root.key"), certPassword); - Path rootCert = createCertificate(request, keyFile, tmpDir.resolve("root.crt"), certPassword); + Path keyFile = createKeyfile(request, tmpDir.resolve("certificate.key"), certPassword); if ( request.getType() == RequestType.ROOT_AUTHORITY || request.getType() == RequestType.STANDALONE_CERTIFICATE ) { - String fingerprint = getCertificateFingerprint(rootCert); - return new OpenSSLCertificateResult(tmpDir, rootCert, keyFile, fingerprint); + Path certificate = createCertificate(request, keyFile, tmpDir.resolve("certificate.crt"), certPassword); + String fingerprint = getCertificateFingerprint(certificate); + passwordProvider.setPasswordFor(fingerprint, certPassword); + return new OpenSSLCertificateResult(tmpDir, certificate, keyFile, fingerprint); } - Path childKey = createKeyfile(request, tmpDir.resolve("child.key"), certPassword); - Path unsignedCert = createSigningRequest(request, childKey, tmpDir.resolve("child.csr"), certPassword); - Path signedCert = signCertificate(request, rootCert, keyFile, unsignedCert, certPassword); - String fingerPrint = getCertificateFingerprint(signedCert); - passwordProvider.setPasswordFor(fingerPrint, certPassword); - return new OpenSSLCertificateResult(tmpDir, signedCert, childKey, fingerPrint); + try (var certAuthority = certificateProvider.requestCertificateUsage(request.getTrustingAuthority())) { + Path unsignedCert = createSigningRequest(request, keyFile, tmpDir.resolve("child.csr"), certPassword); + Path signedCert = signCertificate(request, certAuthority.certificatePath(), certAuthority.certificateKeyPath(), unsignedCert, certPassword); + String fingerprint = getCertificateFingerprint(signedCert); + passwordProvider.setPasswordFor(fingerprint, certPassword); + return new OpenSSLCertificateResult(tmpDir, signedCert, keyFile, fingerprint); + } } private Path createKeyfile(CertificateRequest request, Path outFile, String filePassword) diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java index 67f3345..f7b70d8 100644 --- a/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLCertificateCreator.java @@ -11,19 +11,24 @@ import org.junit.jupiter.api.Test; class TestOpenSSLCertificateCreator { - private OpenSSLCertificateCreator openSSLCertificateCreator; + private CertPasswordProvider passwordProvider; @BeforeEach void setUp() { - CertPasswordProvider passwordProvider = mock(CertPasswordProvider.class); + passwordProvider = mock(CertPasswordProvider.class); when(passwordProvider.generateNewPassword()).thenReturn("ABC-123"); - - ExecutableResolver executableResolver = new ExecutableResolver(); - openSSLCertificateCreator = new OpenSSLCertificateCreator(executableResolver, passwordProvider); } @Test void testCertificateCreation() throws Exception { + CertificateProvider certificateProvider = mock(CertificateProvider.class); + ExecutableResolver executableResolver = new ExecutableResolver(); + var certificateCreator = new OpenSSLCertificateCreator( + executableResolver, + passwordProvider, + certificateProvider + ); + CertificateRequest certRequest = CertificateRequest .builder() .commonName("test.home") @@ -32,8 +37,8 @@ class TestOpenSSLCertificateCreator { .extension(CertificateRequestExtension.builder().alternativeNames("test2.home", "test3.home")) .build(); - try (var cert = openSSLCertificateCreator.createCertificate(certRequest)) { - assertThat(openSSLCertificateCreator.verifyCertificate(cert.getCertificatePath())).isEqualTo(true); + try (var cert = certificateCreator.createCertificate(certRequest)) { + assertThat(certificateCreator.verifyCertificate(cert.getCertificatePath())).isEqualTo(true); System.out.println("Certificate created: " + cert); } }