How to use SSL with FIX Antenna C++ and FIX Antenna .NET
Overview
SSL feature was added to FIX Antenna C++ in version 2.13.0.
The following parameters were added for configuring SSL sessions:
- Engine::SessionExtraParameters::sslContext_Â in FIX Antenna C++.
- com::b2bits::fixantenna::sessionExtraParameters::SSLContext in FIX Antenna .NET.
But FIX Antenna 2.26.0. has updated SSL support, extending the variety of formats accepted and password-protected certificates/keys support.
Now FIX Antenna support certificates in PEM, PFX, and DER formats along with private keys in PEM and DER formats. Password-protected certificates/keys can be used as well. The password is specified within plain text in the engine.properties file.
Starting from version 2.26 SSL interface has been slightly changed:
- com::b2bits::fixantenna::sessionExtraParameters::SSLContext FIX Antenna .NET object got retired and doesn't exist any more.
- Engine::SessionExtraParameters::sslContext_ FIX Antenna C++ object has lost configuration routines such as loadCertificateAndPrivateKey() and informational properties such as certFileName_ and privKeyFileName_.
- New FIX Antenna C++ interface to configure SSL has been introduced System::SSLContextConfigurator (along with accompanying Engine::SSLContextConfiguratorConverter )which is used to pass certificates, keys, and so on to the SSL engine.
- FIX Antenna C++ Engine::FAProperties::getSSLContextConfiguratorInstance() API static routine has been introduced. This routine returns newly created instance of System::SSLContextConfigurator using built in implementation.
Please refer to the SSL parameters page to see the full list of SSL properties in the engine.properties file.Â
SSL Configuration
SSL configuration for Acceptor session
Parameters for SSL Acceptor are configured in the engine.properties file and can't be changed after Engine initialization. They are common for all SSL-enabled acceptors.Â
Since some security issues with SSL v3 and RC4 encryption algorithms have been discovered recently and TLS 1.0, and TLS 1.1 also contain known vulnerabilities, we strongly recommend using TLS 1.2 protocol.
Typical configuration:
######### Engine wide properties ######### # Used for all acceptor sessions and as default values for Initiator sessions. if ListenSSLPort is specified and optional otherwise. # Mandatory. Set protocols accepted by the engine. SSLProtocols = TLSv1_2 # Optional. Engine default values used if omitted. SSLCiphersList = AES+aRSA:AES+aECDH:AES+aECDSA:@STRENGTH # mandatory. Certificate file to use. SSLCertificate = mycert.pem # Optional. Applicable if only certificates is password protected. SSLCertificatePassword = mycertPassword # Optionality is correlated with SSLCertificate. # It means if no SSLCertificate specified then this property becomes seamless. It becomes optional if private key is already contained inside SSLCertificate file. # This is possible for PEM and PFX formats but not for DER! SSLPrivateKey = mycertKey.pem # Optional if private key is not password protected. Please pay attention to the fact the this password is used for private key stored within certificate file as well. # So it has meaning even if SSLPrivateKey is empty in this case. SSLPrivateKeyPassword = mycertKeyPassword # Optional if no peer certificate validation is required. This is a path to the file containing CA certificates (all in one file on after another, PEM format only!). # This certificates are used to build accepted certificates list sent to peer as well. SSLCACertificate = CACerts.pem # Optional. false by default. Setting this property to true requires SSL engine to validate counter-party's certificate against CAs provided. # If validation has failed or no certificate was provided by counter-party connection is terminated immediately. # Set this to true in order to accept only connection verified with certificate. SSLValidatePeerCertificate = true ######### ESVR/CTG Acceptor session ######### # Restricts the session to accept connections from SSL listener only. Session.ESVR/CTG.SSL = true # All remaining properties are engine wide for the acceptors and can't be specified on per-session basis. # So they are configured with engine wide properties.
SSL configuration for Initiator session in Antenna C++
To enable SSL in the Initiator session create SSL Client Context in extra parameters before creating the session:
FIXApp application; Engine::SessionExtraParameters params; params.sslContext_ = System::SSLClientContext(); Engine::Session* pSI = Engine::FixEngine::singleton()->createSession( &application, "TargetCompID", "SenderCompID", Engine::FIX44, ¶ms );
For advanced configuration use System::SSLClientContext::loadCertificateAndPrivateKey method to specify certificate, private key paths. Set the last boolean value to true to check if a private key matches the server certificate.
System::SSLClientContext sslContext; sslContext.loadCertificateAndPrivateKey("cert.pem", "cert.pem", true); params.sslContext_ = sslContext;
SSL configuration for Initiator session in FIX Antenna .NET.
To enable SSL in the Initiator session create the session with the SSLContext parameter.
SessionExtraParameters @params = new SessionExtraParameters(); Â Â Â SessionExtraParameters.SSLContextParameters sslContext = new SessionExtraParameters.SSLContextParameters();Â @params.SSLContext = sslContext;Â Â Session session = FixEngine.Instance.CreateSession(new SessionId("Sender", "Target", null), FixVersion.FIX50SP2, @params, MessageStorageType.Persistent);
For advanced configuration also specify SSL protocols to use, certificate, and private key paths, and set a boolean value to check if a private key matches the server certificate.
sslContext.SSLProtocolsUsed = SSLProtocols.TLSv1 | SSLProtocols.TLSv1_1 | SSLProtocols.TLSv1_2; sslContext.SSLCertificate = "cert.pem";Â sslContext.SSLPrivateKey = "cert.pem";Â sslContext.SSLCheckPrivateKey = true;
Ciphers configuration in FIXÂ Antenna C++ based applications
The allowed ciphers list can be configured with SSLCiphersList in the engine.properties file.
This parameter is optional. The default OpenSSL built-in configuration is used in case of parameter absence.
The list is passed as is to OpenSSL during engine initialization in case of acceptor sessions (acceptor sessions are sharing the same configuration) and during session initialization in case of initiator sessions. See a list of the supported ciphers and the list format is at the official OpenSSL site.
SSLCiphersList = AES+aRSA:AES+aECDH:AES+aECDSA:@STRENGTH
C++ simple example
int main(int argc, char **argv) { try { Engine::FixEngine::InitParameters params; params.propertiesFileName_ = "engine.properties"; Â // Initializes engine. Engine::FixEngine::init( params ); //FIXApp class derived from Engine::Application FIXApp acceptor; Engine::SessionExtraParameters params; Â // Create FIX session instance Engine::Session* pSI = Engine::FixEngine::singleton()->createSession( &acceptor, "SenderCompID", "TargetCompID", Engine::FIX44); // Connect session as acceptor pSI ->connect(); Â //wait for data in acceptor string cmd; while( "exit" != cmd ) { cout << "Type 'exit' to exit > "; getline( cin, cmd ); } // disconnect and release pSI->disconnectNonGracefully(); pSI->release(); } catch( const std::exception& ex ) { cout << "Error: " << ex.what() << endl; } catch ( ... ) { cout << "Unknown error." << endl; } // stop engine Engine::FixEngine::destroy(); return 0; }
int main(int argc, char **argv) { try { // Initialize engine. Engine::FixEngine::init( "engine.properties" ); //FIXApp class derived from Engine::Application FIXApp initiator; Â // Create FIX session instance with SSL. Engine::SessionExtraParameters params; params.sslContext_ = System::SSLClientContext(); Engine::Session* pSI = Engine::FixEngine::singleton()->createSession( &initiator, "TargetCompID", "SenderCompID", Engine::FIX44, ¶ms ); // Connect session as initiator to LISTENER_IP address pSI->connect( 30, LISTENER_IP, 9107); Â // create FIX 4.4 New Order Single using the Flat model std::auto_ptr<Engine::FIXMessage> pMessage( Engine::FIXMsgFactory::singleton()->newSkel( Engine::FIX44, "D" ) ); string clordid; Engine::UTCTimestamp::now().toFixString( &clordid ); pMessage->set( FIXField::ClOrdID, clordid ); pMessage->set( FIXField::Symbol, "MSFT" ); pMessage->set( FIXField::Side, '1' ); // Buy pMessage->set( FIXField::OrderQty, 400 ); pMessage->set( FIXField::OrdType, '2' ); // Limit pMessage->set( FIXField::Price, Engine::Decimal( 1132, -2 ) ); pMessage->set( FIXField::TransactTime, Engine::UTCTimestamp::now() ); // Send order to session initiator pSI->put( pMessage.get() ); Â // Wait message processed. System::Thread::sleep(2000); // disconnect and release session pSI->disconnect(); pSI->release(); } catch( const std::exception& ex ) { cout << "Error: " << ex.what() << endl; } catch ( ... ) { cout << "Unknown error." << endl; } // stop engine Engine::FixEngine::destroy(); return 0; }
C++ advanced exampleÂ
Also samples ./samples/FIX_QuickStart in FIX Antenna C++ package  or ./samples/SimpleClient FIX Antenna .NET in package show how to create session throught SSL connection.
reference
Please read FIX Antenna Quick Start Guide and Installation Guide for information about creating application with FIX Antenna C++.
Migration to version 2.26.0 FIX Antenna C++
The following properties have changed their names or been removed. Please modify the code accordingly if you use them.
- Engine::FIXPropertiesNames::LISTEN_SSL_PROTOCOLS_PARAM→ Engine::FIXPropertiesNames::SSL_PROTOCOLS_PARAM
- Engine::FIXPropertiesNames::LISTEN_SSL_CIPHERS_LIST_PARAM→ Engine::FIXPropertiesNames::SSL_CIPHERS_LIST_PARAM
- Engine::FIXPropertiesNames::LISTEN_SSL_CERTIFICATE_PARAM→ Engine::FIXPropertiesNames::SSL_CERTIFICATE_PARAM
- Engine::FIXPropertiesNames::LISTEN_SSL_PRIVATE_KEY_PARAM→ Engine::FIXPropertiesNames::SSL_PRIVATE_KEY_PARAM
- Engine::FIXPropertiesNames::LISTEN_SSL_CA_CERTIFICATE_PARAM→ Engine::FIXPropertiesNames::SSL_CA_CERTIFICATE_PARAM
- Engine::FIXPropertiesNames::LISTEN_SSL_REQ_CLIENT_CERT_PARAM → Engine::FIXPropertiesNames::SSL_VALIDATE_PEER_CERT_PARAM
- Engine::FIXPropertiesNames::LISTEN_SSL_CHECK_PRIVATE_KEY_PARAMÂ has retired no longer defined. Execution flow goes that way as this property is always true.
Acceptor's migration is transparent. Since all acceptors share the same configuration defined globally no changes are required.
Initiator's migration is not complicated. See the sample below.
System::SSLClientContext sslContext; sslContext.loadCertificateAndPrivateKey("cert.pem", "cert.pem", true); params.sslContext_ = sslContext
System::SSLContextConfiguratorPtr pSSLContextConfigurator = Engine::FAProperties::getSSLContextConfiguratorInstance(0, "", "", "cert.pem", "cert.pem", "" ,"", false); System::SSLClientContext sslContext(Engine::FixEngine::singleton()->getProperties()->getSSLUseProtocols(), pSSLContextConfigurator ); params.sslContext_ = sslContext;
Migration to version 2.26.0 FIX Antenna .NET
Acceptor's migration is transparent. Since all acceptors share the same configuration defined globally no changes are required.
if you want to limit sessions to an SSL socket only set the SessionExtraParameters SSL property to true. See the sample for initiators below. The same flow applies to acceptors as well.
Initiator's migration is not complicated. See the sample below.
SessionExtraParameters @params = new SessionExtraParameters(); SessionExtraParameters.SSLContextParameters sslContext = new SessionExtraParameters.SSLContextParameters(); sslContext.SSLProtocolsUsed = SSLProtocols.TLSv1 | SSLProtocols.TLSv1_1 | SSLProtocols.TLSv1_2; sslContext.SSLCertificate = "cert.pem"; sslContext.SSLPrivateKey = "cert.pem"; sslContext.SSLCheckPrivateKey = true; @params.SSLContext = sslContext; Session session = FixEngine.Instance.CreateSession(new SessionId("Sender", "Target", null), FixVersion.FIX50SP2, @params, MessageStorageType.Persistent);
SessionExtraParameters @params = new SessionExtraParameters(); @params.SSL = true; @params.SSLProtocolsUsed = SSLProtocols.TLSv1 | SSLProtocols.TLSv1_1 | SSLProtocols.TLSv1_2; @params.SSLCertificate = "cert.pem"; @params.SSLPrivateKey = "cert.pem"; Session session = FixEngine.Instance.CreateSession(new SessionId("Sender", "Target", null), FixVersion.FIX50SP2, @params, MessageStorageType.Persistent);