diff --git a/quickfixj-core/src/main/doc/usermanual/usage/secure_communications.html b/quickfixj-core/src/main/doc/usermanual/usage/secure_communications.html index af4837186..b0a708c0d 100644 --- a/quickfixj-core/src/main/doc/usermanual/usage/secure_communications.html +++ b/quickfixj-core/src/main/doc/usermanual/usage/secure_communications.html @@ -113,5 +113,57 @@
quickfix.mina.ssl.SecureSocketTest and quickfix.mina.ssl.SSLCertificateTest.
+By default, QuickFIX/J loads the key and trust stores named in the settings from the file system or
+classpath. An application can instead supply the SSLContext itself through an
+SSLContextProvider, e.g. when the stores come from a secrets manager and should stay in
+memory. For example, with stores provided as Base64-encoded environment variables:
+char[] password = System.getenv("FIX_KEYSTORE_PASSWORD").toCharArray();
+KeyStore keyStore = KeyStore.getInstance("PKCS12");
+keyStore.load(new ByteArrayInputStream(Base64.getMimeDecoder().decode(System.getenv("FIX_KEYSTORE_B64"))), password);
+KeyStore trustStore = KeyStore.getInstance("PKCS12");
+trustStore.load(new ByteArrayInputStream(Base64.getMimeDecoder().decode(System.getenv("FIX_TRUSTSTORE_B64"))),
+ System.getenv("FIX_TRUSTSTORE_PASSWORD").toCharArray());
+
+KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+kmf.init(keyStore, password);
+TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+tmf.init(trustStore);
+SSLContext sslContext = SSLContext.getInstance("TLS");
+sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+SocketInitiator initiator = SocketInitiator.newBuilder()
+ // ... application, store factory, settings
+ .withSSLContextProvider(sslConfig -> sslContext)
+ .build();
+
+
+The provider is set with withSSLContextProvider on the builders of
+SocketInitiator, SocketAcceptor and their threaded variants; without it, the
+configured stores are loaded as before. The provider receives the
+SSLConfig of the session or acceptor address, so it can return different contexts, e.g. a
+different client certificate per counterparty, and delegate to
+SSLContextFactory.getInstance(sslConfig) for stores that should still be loaded from files.
SocketUseSSL=Y is still required. Only the context comes from the provider; protocols,
+cipher suites, NeedClientAuth, SNI and endpoint identification are still applied from the
+settings, while store settings are not used.GeneralSecurityException or returns null, the session
+(initiator) or listening address (acceptor) fails to initialize, subject to
+ContinueInitializationOnError. There is no implicit fallback to the default behavior.SSLContextFactory have their trust managers wrapped so that a
+RuntimeException thrown during certificate validation is reported as a
+CertificateException. A supplied context is used as is; wrap its trust managers with
+X509TrustManagerWrapper.wrap(...) to get the same behaviour.