Been bashing my head against the wall for a while on this one.
I'm trying to setup a quickfix initiator to establish an SSL connection to a Bloomberg FIX server. And based on the docs, I thought this would be relatively straight forward to achieve. However, I can't seem to figure out where I'm going wrong. Any assistance would be super appreciated.
Things to note:
- I'm using the dotnet cli to create, build and run the project
- Bloomberg has provided us with certificates in 3 formats: JKS, PEM & PKCS12
- I've gone ahead and generated the resulting
mycerts.cer
file from the provided PKCS12 certificate using the process described in this S/O comment (Convert PFX to CER). However, the error message is the same whether or not this step is performed - It also might be worth noting that I've previously managed to establish and maintain the secure connection to the bloomberg FIX server using the python version of quickfix as well as stunnel (to handle the SSL connection). However I now need to remove the dependency on stunnel, hence the attempted switch to quickfix/n
The setup:
Here's the current .csproj
file
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Heimdall.AppTest\Heimdall.AppTest.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="QuickFIXn.Core" Version="1.10.0" />
<PackageReference Include="QuickFIXn.FIX4.4" Version="1.10.0" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Copy SourceFiles=".\FIX44.xml" DestinationFolder="$(TargetDir)" />
<Copy SourceFiles=".\bbgEtomsClient.cfg" DestinationFolder="$(TargetDir)" />
<Copy SourceFiles=".\pkcs12\cert.pfx" DestinationFolder="$(TargetDir)" />
<Copy SourceFiles=".\openssl\mycerts.cer" DestinationFolder="$(TargetDir)" />
</Target>
</Project>
And here's the actual bbgEtomsClient.cfg
file that is used by the initiator application (based off this issue)
[DEFAULT]
ApplicationID=client
ConnectionType=initiator
HeartBtInt=60
ResetOnLogon=N
ResetSeqNumFlag=N
FileStorePath=incoming
FileLogPath=outgoing
ScreenLogShowIncoming=Y
ScreenLogShowOutgoing=Y
ScreenLogShowEvents=Y
UseDataDictionary=Y
DataDictionary=FIX44.xml
SocketConnectPort=xxxx
SocketConnectHost=xxx.xx.xxx.xx (same as SSLServerName)
SSLEnable=Y
SSLProtocols=Tls12
SSLCheckCertificateRevocation=N
SSLValidateCertificates=N
SSLServerName=xxx.xx.xxx.xx
SSLCertificate=cert.pfx
SSLCertificatePassword=<PfxCertPassword>
SSLCACertificate=mycerts.cer
[SESSION]
BeginString=FIX.4.4
SenderCompID=<SenderCompID>
TargetCompID=<TargetCompID>
StartTime=00:00:01
EndTime=23:59:59
And here's the super basic program.cs
using System;
using System.Collections.Generic;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using QuickFix.Transport;
using System.Linq;
namespace Heimdall.App
{
public class MyQuickFixApp : IApplication
{
public void FromApp(Message msg, SessionID sessionID)
{
Console.WriteLine("IN:<FromApp> " + msg);
}
public void OnCreate(SessionID sessionID) { }
public void OnLogout(SessionID sessionID) { }
public void OnLogon(SessionID sessionID) { }
public void FromAdmin(Message msg, SessionID sessionID)
{
Console.WriteLine("IN:<FromAdmin> " + msg);
}
public void ToAdmin(Message msg, SessionID sessionID)
{
Console.WriteLine("OUT:<ToAdmin> " + msg);
}
public void ToApp(Message msg, SessionID sessionID)
{
Console.WriteLine("IN:<ToApp> " + msg);
}
}
class Program
{
static void Main(string[] args)
{
SessionSettings settings = new SessionSettings("bbgEtomsClient.cfg");
IApplication myApp = new MyQuickFixApp();
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
ILogFactory logFactory = new FileLogFactory(settings);
SocketInitiator initiator = new SocketInitiator(
myApp,
storeFactory,
settings,
logFactory);
try
{
initiator.Start();
while (true)
{
System.Threading.Thread.Sleep(2000);
}
}
catch (System.Exception e)
{
Console.WriteLine("==FATAL ERROR==");
Console.WriteLine(e.ToString());
initiator.Stop();
}
}
}
}
The errors:
When the project is run using the setup listed above, the following error is observed:
20210107-13:23:00.242 : Unexpected exception: System.ArgumentNullException: Value cannot be null. (Parameter 'value')
at System.Collections.CollectionBase.OnValidate(Object value)
at System.Security.Cryptography.X509Certificates.X509CertificateCollection.OnValidate(Object value)
at System.Collections.CollectionBase.System.Collections.IList.Add(Object value)
at System.Security.Cryptography.X509Certificates.X509CertificateCollection.Add(X509Certificate value)
at QuickFix.Transport.StreamFactory.SSLStreamFactory.GetClientCertificates()
at QuickFix.Transport.StreamFactory.SSLStreamFactory.CreateClientStreamAndAuthenticate(Stream innerStream)
at QuickFix.Transport.StreamFactory.CreateClientStream(IPEndPoint endpoint, SocketSettings settings, ILog logger)
at QuickFix.SocketInitiatorThread.SetupStream()
at QuickFix.Transport.SocketInitiator.SocketInitiatorThreadStart(Object socketInitiatorThread)
And from what I can understand it looks like my error might be related to this issue (.NetCore3.1 and QuickFIXn problem #571)