Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion QuickFIXn/Transport/SslStreamFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,33 @@ internal bool VerifyRemoteCertificate(

// If CA Certificate is specified then validate against the CA certificate, otherwise it is validated against the installed certificates
Copy link
Contributor Author

@dckorben dckorben Dec 23, 2025

Choose a reason for hiding this comment

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

Notable that this comment SAYS it validates against installed certificates but that is actually not true. It validates against installed certificates if it is a HTTPS/TLS connection, which was probably the intended meaning. If you specify a certificate but not a CA, it just fails. In the case of #990, you can specify a certificate from a Public CA but then validation fails and because many CAs use a lineage of certificates you cannot validate the chain with the existing configuration options.

if (string.IsNullOrEmpty(_socketSettings.CACertificatePath)) {
_nonSessionLog.Log(LogLevel.Warning, "CACertificatePath is not specified");
_nonSessionLog.Log(LogLevel.Warning, "CACertificatePath is not specified, using machine store");

X509Chain chain = new();
chain.ChainPolicy.RevocationMode = _socketSettings.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck;

bool isValid = chain.Build((X509Certificate2)certificate);
if (isValid)
{
bool isChainValid = true;
foreach (var status in chain.ChainStatus)
{
if (!status.Status.HasFlag(X509ChainStatusFlags.NoError))
{
isChainValid = false;
break;
}
}
if (isChainValid)
// resets the sslPolicyErrors.RemoteCertificateChainErrors status
sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
else
sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors;
}
else
{
sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors;
}
}
else
{
Expand Down
37 changes: 36 additions & 1 deletion UnitTests/SslStreamFactoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ public class SslStreamFactoryTest
const string ServerCertificatePath = "serverCertificate.cer";
const string ClientCertificatePath = "clientCertificate.cer";

const string CaIntermediateCertificatePath = "CaIntermediateCertificate.cer";
const string ServerIntermediateCertificatePath = "serverIntermediateCertificate.cer";
const string ClientIntermediateCertificatePath = "clientIntermediateCertificate.cer";

X509Certificate2 CaCertificate { get; set; } = null!;
X509Certificate2 ClientCertificate { get; set; } = null!;
X509Certificate2 ServerCertificate { get; set; } = null!;

X509Certificate2 CaIntermediateCertificate { get; set; } = null!;
X509Certificate2 ClientIntermediateCertificate { get; set; } = null!;
X509Certificate2 ServerIntermediateCertificate { get; set; } = null!;

[OneTimeSetUp]
public void BuildCerts()
{
Expand All @@ -37,6 +45,20 @@ public void BuildCerts()
File.WriteAllBytes(ClientCertificatePath, clientCertificate.Export(X509ContentType.Cert));
ClientCertificate = clientCertificate;


var caIntermediateCertificate = CreateCACertificate(caCertificate);
File.WriteAllBytes(CaIntermediateCertificatePath, caIntermediateCertificate.Export(X509ContentType.Cert));
CaIntermediateCertificate = caIntermediateCertificate;

var serverIntermediateCertificate = CreateServerCertificate(caIntermediateCertificate);
File.WriteAllBytes(ServerIntermediateCertificatePath, serverIntermediateCertificate.Export(X509ContentType.Cert));
ServerIntermediateCertificate = serverIntermediateCertificate;

var clientIntermediateCertificate = CreateClientCertificate(caIntermediateCertificate);
File.WriteAllBytes(ClientIntermediateCertificatePath, clientIntermediateCertificate.Export(X509ContentType.Cert));
ClientIntermediateCertificate = clientIntermediateCertificate;


var differentCaCertificate = CreateCACertificate();
File.WriteAllBytes(DifferentCaCertificatePath, differentCaCertificate.Export(X509ContentType.Cert));
}
Expand All @@ -58,10 +80,23 @@ static X509Certificate2 CreateCACertificate()
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, true));
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));

X509Certificate2 certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(5));
X509Certificate2 certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(10));
return certificate;
}

static X509Certificate2 CreateCACertificate(X509Certificate2 caCertificate)
{
var rsa = RSA.Create();
var request = new CertificateRequest("CN=127.0.0.1 Test Intermediate CA", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));

X509Certificate2 certificate = request.Create(caCertificate, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(5), [1, 0, 0, 0, 0, 0, 0, 0]);

var rootCertificate = certificate.CopyWithPrivateKey(rsa);
return rootCertificate;
}

static X509Certificate2 CreateServerCertificate(X509Certificate2 caCertificate)
{
var rsa = RSA.Create();
Expand Down