• Version 1.16.0
Show / Hide Table of Contents

SSL BuySide Sample Project

This sample connects to the pre-defined host and port (acts as a FIX Initiator).

When the session is established, the 'SingleOrder - New' (MsgType='D') FIX message is sent to the counterparty.

This sample shows how to set up SSL/TLS connections on the initiator side using PEM- or PFX-format certificates.

Use --help, -h, -?, /? to view CLI options and their descriptions.

In its original form, SslBuySide successfully validates the SslSellSide certificate, however, using the -i/--interactive-validation argument, it is possible to confirm the success of the certificate validation or refuse it interactively. This case shows the way to subscribe to the RemoteCertificateValidationCallback and how to operate within the callback.

You can also check the reverse scenario, for example, by using an unknown certificate on the SslSellSide side or by interrupting the validation chain on the SslBuySide side by commenting out the line:

// ssl.CaFile = ...

In this case, the pre-validation of the certificate will fail, but interactively it will be possible to accept an invalid certificate.

© Onix Solutions

Source code


using System;
using System.CommandLine;
using OnixS.Fix;
using System.Globalization;
using System.IO;
using OnixS.Fix.Fix44;
using System.Net;
using NLog.Extensions.Logging;

namespace SslBuySide
{
    /// <summary>
    /// Establishes the FIX session as Initiator.
    /// </summary>
    internal static class Initiator
    {
        private const ProtocolVersion fixVersion = ProtocolVersion.Fix44;

        private static string GetLicenseStoreFolder()
        {
            string path = Path.Join(AppContext.BaseDirectory, "../../../../../license");

            if (Directory.Exists(path))
                return path;

            // expecting it run after `dotnet publish` using default paths
            return Path.Join(AppContext.BaseDirectory, "../../../../../../license");
        }

        private static System.Security.SecureString MakeSecureString(string input)
        {
            System.Security.SecureString result = new();

            foreach (char c in input)
                result.AppendChar(c);

            return result;
        }

        private static Message CreateOrderMessage(IMessageInfoDictionary dictionary)
        {
            Message order = new(MsgType.NewOrderSingle, dictionary);

            order.Set(Tag.HandlInst, HandlInst.AutoExecPub)
                 .Set(Tag.ClOrdID, "Unique identifier for Order")
                 .Set(Tag.Symbol, "TSLA")
                 .Set(Tag.Side, Side.Buy)
                 .Set(Tag.OrderQty, 1000)
                 .Set(Tag.OrdType, OrdType.Market)
                 .Set(Tag.TransactTime, DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss", CultureInfo.InvariantCulture));

#if DEBUG
            order.Validate();
#endif

            return order;
        }

        static bool AskForAcceptingCertificate(bool defaultValue)
        {
            while (true)
            {
                string defaultAnswer = defaultValue ? "yes" : "no";
                Console.Write($"\nAccept this certificate? Y[es]/N[o] (blank for {defaultAnswer}): ");

                switch (Console.ReadLine()?.ToUpper())
                {
                case "Y": case "YES": return true;
                case "N": case "NO":  return false;
                case "": case null:   return defaultValue;
                }
            }
        }

        private static void Run(bool usePemCertificate, bool verifyPeer, bool interactiveCertValidation)
        {
            Console.WriteLine("SSL Buy Side Sample");

            EngineSettings settings = new()
            {
                LicenseStore = GetLicenseStoreFolder(),
                LoggerProvider = new NLogLoggerProvider()
            };

            Engine.Init(settings);

            var dictionary = fixVersion.ToDictionary();

            using (Session session = new("SslBuySide", "SslSellSide", dictionary))
            {

                session.Encryption = EncryptionMethod.Ssl;

                var ssl = session.Ssl;

                ssl.CertificateLocation = usePemCertificate ? "client.pem|client-pk.pem" : "client.p12";

                if (verifyPeer || interactiveCertValidation)
                {
                    ssl.VerifyPeer = true;
                    ssl.CaFile = usePemCertificate ? "cert-chain.pem" : "cert-chain.p12";

                    if(interactiveCertValidation)
                    {
                        ssl.RemoteCertificateValidationCallback = (object sender, in CertificateValidationArgs args) =>
                        {
                            Console.WriteLine("Server certificate: ");
                            Console.WriteLine(args.Certificate.ToString(false));

                            Console.Write("Pre-verification result: ");

                            if(args.VerificationResult)
                            {
                                Console.WriteLine("OK");

                                if(!AskForAcceptingCertificate(true))
                                {
                                    args.ErrorDescription.Append("Rejected by custom callback");
                                    args.VerificationResult = false;
                                }
                            }
                            else
                            {
                                Console.WriteLine("Failed!!!");

                                if(args.ErrorDescription.Length != 0)
                                {
                                    Console.WriteLine("\nError description:\n" + args.ErrorDescription);
                                }

                                if (AskForAcceptingCertificate(false))
                                {
                                    args.ErrorDescription.Clear();
                                    args.VerificationResult = true;
                                }
                            }
                        };
                    }
                }

                ssl.PrivateKeyPassword = MakeSecureString("OnixS");

                session.InboundApplicationMessage += (object sender, InboundMessageEventArgs e) =>
                {
                    Console.WriteLine("Inbound application-level message:\n" + e.Message);
                };

                session.StateChanged += (object sender, SessionStateChangeEventArgs e) =>
                {
                    Console.WriteLine("Session state: " + e.NewState);
                };

                session.Error += (object sender, SessionErrorEventArgs e) =>
                {
                    Console.WriteLine("Error: " + e);
                };

                session.Warning += (object sender, SessionWarningEventArgs e) =>
                {
                    Console.WriteLine("Warning: " + e);
                };

                session.LogonAsInitiator(IPAddress.Loopback, 10451, 30, true);

                Message order = CreateOrderMessage(dictionary);

                session.Send(order);

                Console.WriteLine($"The order ({order}) was sent");

                session.Logout("The session is disconnected by BuySide");
            }

            Engine.Shutdown();
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static int Main(string[] args)
        {
            try
            {
                var pemOption = new Option<bool>(
                    new[] { "--use-pem-certificate", "-p" },
                    "Option to use PEM certificate instead of default PFX one");

                var verifyPeerOption = new Option<bool>(
                    new[] { "--verify-peer", "-v" },
                    "Specifies if the client should verify the server certificate");

                var interactiveCertValidationOption = new Option<bool>(
                    new[] { "--interactive-validation", "-i" },
                    "The client may validate the server certificate in interactive mode");

                var rootCommand = new RootCommand { pemOption, verifyPeerOption, interactiveCertValidationOption };

                rootCommand.Description = "SSL Buy Side Sample";
                rootCommand.TreatUnmatchedTokensAsErrors = true;
                rootCommand.SetHandler<bool, bool, bool>(Run, pemOption, verifyPeerOption, interactiveCertValidationOption);

                return rootCommand.Invoke(args);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex);

                return 1;
            }
            finally
            {
                // From https://github.com/NLog/NLog/wiki/Tutorial:
                // NET Application running on Mono / Linux are required to stop threads / timers before entering application shutdown phase.
                // Failing to do this will cause unhandled exceptions and segmentation faults, and other unpredictable behavior.
                NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers
            }
        }
    }
}

In this article
Back to top Copyright © Onix Solutions.
Generated by DocFX