package de.mlessmann.certassist; import static org.assertj.core.api.Assertions.assertThat; import de.mlessmann.certassist.keystore.KeyStoreManager; import de.mlessmann.certassist.openssl.CertificateProvider; import de.mlessmann.certassist.openssl.CertificateUsage; import de.mlessmann.certassist.openssl.InMemoryCertificatePasswordProvider; import de.mlessmann.certassist.openssl.OpenSSLService; import de.mlessmann.certassist.service.ExecutableResolver; import java.io.IOException; import java.net.ServerSocket; import java.nio.file.Path; import java.security.SecureRandom; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestKeystoreCreation { private static final String STORE_PASSPHRASE = "changeit"; private static final SecureRandom TEST_RANDOM = new SecureRandom(); private final CertificateUsage dummyCert = new CertificateUsage() { @Override public String fingerprint() { return TestOpenSSLService.TEST_CERT_FINGERPRINT; } @Override public Path certificatePath() { return TestOpenSSLService.TEST_CERT_PATH.resolve("x509forImport.pem"); } @Override public Path certificateKeyPath() { return TestOpenSSLService.TEST_CERT_PATH.resolve("x509forImport.key.pem"); } @Override public Path fullchainPath() { return TestOpenSSLService.TEST_CERT_PATH.resolve("x509forImport.fullchain.pem"); } }; @Test void testTruststore() throws Exception { var passwordProvider = new InMemoryCertificatePasswordProvider(); passwordProvider.setPasswordFor(dummyCert.fingerprint(), TestOpenSSLService.TEST_CERT_PASSPHRASE); var certificateProvider = Mockito.mock(CertificateProvider.class); var opensslCertCreator = new OpenSSLService(new ExecutableResolver(), passwordProvider, certificateProvider); var keyStoreManager = new KeyStoreManager(opensslCertCreator, passwordProvider); AtomicBoolean serverAccepted = new AtomicBoolean(false); AtomicBoolean clientAccepted = new AtomicBoolean(false); try ( var tmpTruststore = keyStoreManager.createTruststore(STORE_PASSPHRASE, dummyCert); var tmpKeyStore = keyStoreManager.createKeyStore(STORE_PASSPHRASE, dummyCert) ) { KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() ); keyManagerFactory.init(tmpTruststore.readAsKeystore(STORE_PASSPHRASE), STORE_PASSPHRASE.toCharArray()); SSLContext tlsSrvContext = SSLContext.getInstance("TLS"); tlsSrvContext.init(keyManagerFactory.getKeyManagers(), null, TEST_RANDOM); ServerSocket serverSocket = tlsSrvContext.getServerSocketFactory().createServerSocket(0); var serverThread = Thread.startVirtualThread(() -> { try { var remote = serverSocket.accept(); serverAccepted.set(true); try { Thread.sleep(2_000); } catch (InterruptedException e) { // nothing } remote.close(); } catch (IOException e) { throw new IllegalStateException("Failed to create server socket!", e); } }); var trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(tmpTruststore.readAsKeystore(STORE_PASSPHRASE)); SSLContext tlsContext = SSLContext.getInstance("TLS"); tlsContext.init(null, trustManagerFactory.getTrustManagers(), TEST_RANDOM); var clientThread = Thread.startVirtualThread(() -> { try { var socket = tlsContext.getSocketFactory().createSocket("127.0.0.1", serverSocket.getLocalPort()); clientAccepted.set(true); socket.close(); } catch (IOException e) { throw new IllegalStateException("Failed to create client socket!", e); } }); serverThread.join(); clientThread.join(); if (!serverSocket.isClosed()) { serverSocket.close(); } assertThat(serverAccepted.get()).withFailMessage("Server did not accept connection!").isTrue(); assertThat(clientAccepted.get()).withFailMessage("Client did not accept connection!").isTrue(); } } }