Skip to content

Fallback to System Certificate Store when CA Certificate Path Is Not Present#991

Draft
dckorben wants to merge 6 commits into
connamara:masterfrom
dckorben:develop990
Draft

Fallback to System Certificate Store when CA Certificate Path Is Not Present#991
dckorben wants to merge 6 commits into
connamara:masterfrom
dckorben:develop990

Conversation

@dckorben
Copy link
Copy Markdown
Contributor

@dckorben dckorben commented Dec 23, 2025

"Fixes" #990

This isn't final, too duplicative. Need someone to verify it works on Windows/Linux as we've been bitten by behavior differences before and I don't have an admin box I can trust the generated CA with.

@dimaaik27 Please verify this works in your implementation before I clean up the code.

[@gbirchmeier added: resolves #990 ]

@dckorben dckorben changed the title Develop990 Fallback to System Cert Store Dec 23, 2025
@@ -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
Copy Markdown
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.

@dckorben dckorben changed the title Fallback to System Cert Store Fallback to System Certificate Store when CA Certificate Path Is Not Present Dec 23, 2025
@dckorben
Copy link
Copy Markdown
Contributor Author

dckorben commented Dec 23, 2025

I do see a potential issue here where it doubles up the certificate chain validation for TLS/HTTPS connections because the function VerifyRemoteCertificate isn't aware of the certificate source.

@dimaaik27
Copy link
Copy Markdown

@dckorben yes it works now if I don't specify CA certificate and it checks it agains the installed ones.

@dckorben
Copy link
Copy Markdown
Contributor Author

Are you a Linux test by chance?

@dckorben
Copy link
Copy Markdown
Contributor Author

@dckorben yes it works now if I don't specify CA certificate and it checks it agains the installed ones.

The good aspect of this is you won't have to manage the CA key expirations manually. So, it's an improvement in function as well.

@dimaaik27
Copy link
Copy Markdown

nope, windows

@Rob-Hague
Copy link
Copy Markdown
Contributor

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.

I've not understood this sentence nor the nuances of the issue, but just to point out: SslStream already performs validation, and RemoteCertificateValidationCallback is just to perform extra validation on top. So at the very least, it seems this method is missing a if (sslPolicyErrors == SslPolicyErrors.None) return true; at the top, which seems like it would fix some things.

Definitely could be missing something, but I believe it should/could look something like:

static bool ValidateServerCertificate(
    object sender,
    X509Certificate certificate,
    X509Chain chain,
    SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        // Cert is trusted. No need to do anything else.
        // (SslStream also already checks for server/client auth EKU)
        return true;
    }

    if (_socketSettings.ValidateCertificates == false)
        return true;

    X509Certificate2 caCert = ...;

    using var customChain = new X509Chain();

    customChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
    customChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    customChain.ChainPolicy.ApplicationPolicy.Add(new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1")); // server auth EKU
    customChain.ChainPolicy.CustomTrustStore.Add(caCert);

    bool isValid = customChain.Build((X509Certificate2)certificate);

    if (!isValid)
    {
        foreach (var status in customChain.ChainStatus)
        {
            // Log errors etc.
        }
    }

    return isValid;
}

@dckorben
Copy link
Copy Markdown
Contributor Author

dckorben commented Apr 7, 2026

Definitely could be missing something, but I believe it should/could look something like:

static bool ValidateServerCertificate(
    object sender,
    X509Certificate certificate,
    X509Chain chain,
    SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        // Cert is trusted. No need to do anything else.
        // (SslStream also already checks for server/client auth EKU)
        return true;
    }

    if (_socketSettings.ValidateCertificates == false)
        return true;

    X509Certificate2 caCert = ...;

    using var customChain = new X509Chain();

    customChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
    customChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    customChain.ChainPolicy.ApplicationPolicy.Add(new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1")); // server auth EKU
    customChain.ChainPolicy.CustomTrustStore.Add(caCert);

    bool isValid = customChain.Build((X509Certificate2)certificate);

    if (!isValid)
    {
        foreach (var status in customChain.ChainStatus)
        {
            // Log errors etc.
        }
    }

    return isValid;
}

That would probably work. Let me take another look.

@gbirchmeier
Copy link
Copy Markdown
Member

Hi guys, I have nothing to add (I hate this SSL stuff!), but wanted to let you know I'm paying attention and looking forward to merging when you get it figured out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Certificate verification fails with chained certificates

4 participants