Skip to content

Pluggable SSLContextProvider for in-memory key and trust stores - #1344

Merged
chrjohn merged 6 commits into
quickfix-j:masterfrom
nkrivenko:feature/1343-ssl-context-provider
Oct 28, 2026
Merged

chrjohn merged 6 commits into
quickfix-j:masterfrom
nkrivenko:feature/1343-ssl-context-provider

Conversation

@nkrivenko

@nkrivenko nkrivenko commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a pluggable SSLContextProvider so applications can supply the SSLContext for SSL-enabled sessions instead of having SSLContextFactory load key and trust stores from files. This lets key material held in memory, e.g. delivered by a secrets manager as environment variables, be used without writing it to disk.

Closes #1343. Follows the approach suggested in #678 / discussion #679.

Changes

  • SSLContextProvider (quickfix.mina.ssl): a functional interface, SSLContext getSSLContext(SSLConfig). The default is SSLContextFactory::getInstance, so existing behaviour is unchanged.
  • withSSLContextProvider(...) on the builders of SocketInitiator, SocketAcceptor and their threaded variants. Without it (or with null), the builder uses SSLContextFactory::getInstance.
  • The provider is held in a final, never-null field of AbstractSocketAcceptor / AbstractSocketInitiator, set through new constructor overloads; the existing constructors use the default.
  • Failure handling: if the provider throws GeneralSecurityException, the session (initiator) or listening address (acceptor) fails to initialize, honouring ContinueInitializationOnError. The provider must not return null. There is no silent fallback to file stores.
  • Only the SSLContext comes from the provider; protocols, cipher suites, NeedClientAuth, SNI and endpoint identification are still applied from the settings.
  • User manual: new "Supplying SSL contexts from the application" section in secure_communications.html.

Usage

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 receives the SSLConfig of each session or acceptor address, so it can also return different contexts per session, or delegate to SSLContextFactory.getInstance(sslConfig) for stores that should still be loaded from files.

Compatibility

  • No behaviour change for existing users; all new API is additive. SSLContextFactory and SessionConnector are unchanged, and connectors created with the existing constructors keep loading stores from files.
  • The existing package-private IoSessionInitiator constructor is kept and delegates to the new one.
  • Java 8 compatible, no new dependencies.

Testing

New SSLContextProviderTest:

  • mutual TLS logon with contexts built from in-memory PKCS12/JCEKS stores, using both the plain and the threaded connector builders;
  • without a provider, the configured stores are loaded from files as before;
  • initiator and acceptor start fail when the provider throws GeneralSecurityException;
  • with ContinueInitializationOnError=Y, a failing provider skips the acceptor address and the initiator session.

Existing SSLCertificateTest, SecureSocketTest, IoSessionInitiatorTest, SocketInitiatorTest and SocketAcceptorTest pass unchanged.

@nkrivenko
nkrivenko marked this pull request as ready for review October 14, 2026 07:20
@codecov

codecov Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.75%. Comparing base (e6d9059) to head (f1e0f22).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
...quickfix/mina/acceptor/AbstractSocketAcceptor.java 75.00% 2 Missing ⚠️
...ickfix/mina/initiator/AbstractSocketInitiator.java 66.66% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1344      +/-   ##
============================================
+ Coverage     70.78%   71.75%   +0.97%     
- Complexity     2272     2306      +34     
============================================
  Files           162      162              
  Lines          9087     9104      +17     
  Branches       1192     1192              
============================================
+ Hits           6432     6533     +101     
+ Misses         2201     2119      -82     
+ Partials        454      452       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nkrivenko
nkrivenko force-pushed the feature/1343-ssl-context-provider branch from d4dd222 to 93736f6 Compare October 14, 2026 08:44

@the-thing the-thing left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful feature. Can we just make the field final and if specific provider logic is required can we make it in the builder?

Comment thread quickfixj-core/src/main/java/quickfix/mina/acceptor/AbstractSocketAcceptor.java Outdated
Comment thread quickfixj-core/src/main/java/quickfix/mina/SessionConnector.java Outdated
Comment thread quickfixj-core/src/main/java/quickfix/mina/SessionConnector.java Outdated
Comment thread quickfixj-core/src/main/java/quickfix/SocketAcceptor.java Outdated
Comment thread quickfixj-core/src/main/java/quickfix/SocketInitiator.java Outdated
Comment thread quickfixj-core/src/main/java/quickfix/ThreadedSocketAcceptor.java Outdated
Comment thread quickfixj-core/src/main/java/quickfix/ThreadedSocketInitiator.java Outdated
@nkrivenko

Copy link
Copy Markdown
Contributor Author

Hello @the-thing , thank you for your comments, moved everything to builders and made the field final. Could you please review again when you have a chance?

@the-thing

Copy link
Copy Markdown
Collaborator

Will do. It might take a few days, but I will. Stay tuned.

@the-thing the-thing left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

SSLContextProviderTest start / stop could have been wrapped in separate try / finally blocks.

Not for this PR, but I have a feeling that these overloaded super constructors are too large and probably every one of them should have a single parameter builder and take whatever they need from there.

@nkrivenko

Copy link
Copy Markdown
Contributor Author

Thank you @the-thing , addressed this comment:

SSLContextProviderTest start / stop could have been wrapped in separate try / finally blocks.

Also will take a look whether we can replace the ctors' parameters with builders, will create an issue for that if this is feasible.

Allow applications to supply the SSLContext for SSL-enabled sessions
instead of having SSLContextFactory load stores from files, e.g. when
key material is delivered by a secrets manager as environment variables.

- Add SSLContextProvider, keyed by SSLConfig; the default is
  SSLContextFactory::getInstance, so existing behaviour is unchanged.
- Add setSSLContextProvider on SessionConnector and
  withSSLContextProvider on the connector builders.
- Report provider failures and null contexts as initialization errors,
  honouring ContinueInitializationOnError; no fallback to file stores.
- Document usage in the secure communications user manual.

Relates to quickfix-j#1343, quickfix-j#678
Address review: remove SessionConnector.setSSLContextProvider and the
wrapping getter, keep the provider in a final, never-null field of the
socket acceptor and initiator, and pass it through their constructors.
The builder defaults the provider to SSLContextFactory::getInstance in
build(), like the log and message factories.
Give each connector in SSLContextProviderTest its own try/finally, so a
failing stop cannot leave the other connector running.

Load the trust store in the user manual example with its password:
PKCS12 trust stores created by current JDKs encrypt their certificates,
so loading them without a password yields an empty trust store.
@nkrivenko
nkrivenko force-pushed the feature/1343-ssl-context-provider branch from 6ec69c7 to 03fcd09 Compare October 24, 2026 09:38
@chrjohn chrjohn changed the title Pluggable SSLContextProvider for in-memory key and trust stores Pluggable SSLContextProvider for in-memory key and trust stores Oct 26, 2026
@chrjohn

chrjohn commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

@nkrivenko

Also will take a look whether we can replace the ctors' parameters with builders, will create an issue for that if this is feasible.

Yes, please do create a separate issue and/or PR. Thank you.

And thank you for this PR and @the-thing for the thorough review. 👍

Comment thread quickfixj-core/src/main/doc/usermanual/usage/secure_communications.html Outdated
A null context used to reach MINA's SslFilter and fail start() with an
NPE that bypassed ContinueInitializationOnError. Report it as a
GeneralSecurityException naming the session or acceptor address, so it
goes through the existing initialization error handling.
@chrjohn chrjohn added this to the QFJ 3.0.3 milestone Oct 28, 2026
@chrjohn
chrjohn merged commit 8612876 into quickfix-j:master Oct 28, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pluggable SSLContextProvider for in-memory keys and/or truststores

3 participants