dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.47k stars 4.76k forks source link

Unix SslStream may not be sending intermediate CA certs when required #17511

Closed bartonjs closed 4 years ago

bartonjs commented 8 years ago

As a followup to dotnet/corefx#9109 I was looking for the equivalent code in SslStream, and there doesn't seem to be any.

This issue is currently "determine if there really is a problem", with the implicit "if there was, fix it".

This is somewhat hard to test, since the only way to get the intermediate certs on the request given the current API (I think) is to have them in a CA store; but if they're in a CA store we'll already find them when we chain walk on the receiver side. If the client API made guarantees about when the chain was walked then it could be [OuterLoop] managed by adding intermediates to the store, setting up the client, removing the certs from the store, and letting the request initiate.

sichbo commented 8 years ago

Can confirm Unix SslStream is broken compared to Windows -- it's not sending intermediate certs even after manually importing an AlphaSSL Intermediate CA via dpkg-reconfigure ca-certificates. This DigiCert verification tool winds up with a "not trusted" result. Testing an endpoint running on Windows with the exact same app works correctly.

This issue has thrown a bit of a spanner in the works, as iOS devices are all raising certificate errors when they hit a Unix instance.

bartonjs commented 8 years ago

Thanks for the field report, @sichbo. I'm glad to hear that you're trying it out; sorry to hear that it didn't work :frowning:.

@ericeil, let me know if you need any help on this.

sichbo commented 8 years ago

I've been looking into this to see if I could identify some kind of interim workaround without having to figure out how to modify the entire framework etc. I'm a fish out of water when it comes to unix, but I think the openssl context might simply need a call somewhere in Interop.OpenSsl.cs through to SSL_CTX_add_extra_chain_cert. The shim is already there for CryptoNative_SslAddExtraChainCert but it's currently not referenced anywhere.

I notice that we can do this without any trouble on a Unix server:

var chain = new X509Chain();
chain.Build(new X509Certificate2(pfxFile, password));
Debug.WriteLine("Certficate chain: {0}", chain.ChainElements.Count);
for (int i = 0; i < chain.ChainElements.Count; i++)
    Debug.WriteLine("{0}: {1}", i, chain.ChainElements[i].Certificate.Subject);

..and the output displays all three certificates OK. So the nuts and bolts must be there :) Perhaps something a long the lines of this in Interop.OpenSsl.cs:

void SetSslCertificate(..., SafeX509Handle[] additionalX509ChainElementPtrs) {
    ..
    int retVal = Ssl.SslCtxUseCertificate(contextPtr, certPtr);
    if (1 != retVal )
      throw CreateSslException(SR.net_ssl_use_cert_failed);

    // Ideally additionalX509ChainElementPtrs should be cached upstream, probably
    // don't want to try X509Chain.Build() every single time a new SSL connection comes in..
    for (int i = 0; i < additionalX509ChainElements.Length; i++)
      if ( 1 != Ssl.CryptoNative_SslAddExtraChainCert(contextPtr, additionalX509ChainElementsPtrs[i]) )
              throw CreateSslException(..);

    ...
}

Good luck!

sichbo commented 8 years ago

This was holding me back a bit so have fixed and uploaded under dotnet/corefx#9810 -- feel free to use or not. Turned out no caching of the chain was necessary as SafeFreeSslCredentials is a one-time instantiation for the life of the TLS server.

Rock on.

bartonjs commented 8 years ago

Hm, I think I meant dotnet/corefx#9131. That looks much more relevant

stephentoub commented 8 years ago

Fixed by dotnet/corefx#9814