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.properties
######### 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:

SSLQuickStart.cpp
FIXApp application;
Engine::SessionExtraParameters params;
params.sslContext_ = System::SSLClientContext();

Engine::Session* pSI =  Engine::FixEngine::singleton()->createSession( &application, "TargetCompID", "SenderCompID",  Engine::FIX44, &params );

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.

SSLQuickStart.cpp
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.

SSLQuickStart.cs
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.

SSLQuickStart.cs
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 feature was added to FIX Antenna C++ in version 2.25.1

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.

engine.properties
SSLCiphersList = AES+aRSA:AES+aECDH:AES+aECDSA:@STRENGTH

C++ simple example

Server.cpp
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;
}
Clent.cpp
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, &params );
        // 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 

Server.cpp
int main(int argc, char **argv)
{
    try {
        Engine::FixEngine::InitParameters params;
        params.propertiesFileName_ = "engine.properties";

		// Setup SSL parameters
        params.properties_[Engine::FIXPropertiesNames::LISTEN_SSL_PORT_PARAM] = "9107";
        params.properties_[Engine::FIXPropertiesNames::LISTEN_SSL_CHECK_PRIVATE_KEY_PARAM] = "true";
        params.properties_[Engine::FIXPropertiesNames::LISTEN_SSL_CERTIFICATE_PARAM] = "cert.pem";
        params.properties_[Engine::FIXPropertiesNames::LISTEN_SSL_PRIVATE_KEY_PARAM] = "cert.pem";
        params.properties_[Engine::FIXPropertiesNames::LISTEN_SSL_PROTOCOLS_PARAM] = "TLSv1, TLSv1_1, TLSv1_2";
 
        // 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, &params);

        // 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;
}
Clent.cpp
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;
        System::SSLClientContext sslContext;
        sslContext.loadCertificateAndPrivateKey("cert.pem", "cert.pem", true);
        params.sslContext_ = sslContext;
        Engine::Session* pSI =  Engine::FixEngine::singleton()->createSession( &initiator, "TargetCompID", "SenderCompID",  Engine::FIX44, &params );
        // 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;
}

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.

Before
System::SSLClientContext sslContext;
sslContext.loadCertificateAndPrivateKey("cert.pem", "cert.pem", true);
params.sslContext_ = sslContext
After
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.

Before
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);
After
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);