-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SslStream only sends chain certs if they're in the Windows Cert Store #26323
Comments
cc: @bartonjs |
...except you're using the file, not a cert from the store, as the client id, right? Very probably it's not loading the entire chain from the file (it only actually loads one), so the leaf has a reference value to the issuer cert (thumbprint? not sure), but no idea what the actual cert is. |
@Clockwork-Muse I'm currently selecting the certificate from an I figured the solution might involve creating some kind of temporary If you have any info on how to create a temporary store only visible to the app and have |
I've checked from Linux, and observed the same behavior - the chain of intermediate certs is sent if I use the My certificate store, but not if I create a unique one with I guess this is happening in |
I've managed to get it working as expected on Linux, by using the
When doing this, Is there anything like this for Windows? @bartonjs, I guess if anyone would know it would be you? ;) |
@cocowalla - well, often, I thought I'd discovered a way to load/send the entire chain when dealing with a different issue, but I can't recreate it now, possibly I just imagined it. What's your usecase here, that you wouldn't be adding the certs to the store anyways? |
Regarding My usecase was a (failed!) attempt to keep things simple by using the same approach on both Windows and Linux (keys stored in the filesystem, protected by ACLs). I've decided to give up; I'll use I do however still think that |
While I agree that it would be nice if |
I don't really see it as side-stepping anything; more augmenting the existing mechanism. The My store would still be used, but the ephemeral store or For your last point, I'd argue that most users would be comfortable replacing a file, and probably haven't even heard of the Windows Certificate Store ;) |
I don't really know the SChannel APIs (which provide TLS for us on Windows), but I think that they only take the single certificate, then internally do the chain building for sending the intermediates. The Linux and macOS TLS APIs are a little more raw, so they might be more amenable to such a feature, but I wouldn't add a new feature that doesn't work on Windows (71% of the usage of .NET Core, as of last summer). |
@bartonjs ah, that explains why I couldn't find anything in I agree it makes no sense to add this unless it works across all platforms. |
I like to bring this issue back to the table. We have a aspnet core based service that should be deployed onto clients windows computers. They are most likely not publicly available on the internet, and the http API that our service provides should be secured. We have a self-signed root CA and an intermediate CA and this issues a certificate for each installation. This way we can also do client-certificate auth with our service as the client. We really want to avoid to install our root and intermediate certificates into the trusted root CA store of the clients computers. We deem this a bad security practice and really want to avoid it. Technically it is sufficient to use certificate pinning to our root CA cert within our dedicated client application. There is no need that any computer should be needed to completely trust our cert. So there needs to be a way to tell kestrel on windows (which uses SslStream) to not only send the actual server certificate we configured but the complete chain, without installing the chain in the trusted root store of the computer. This is pretty counter intuitive if you create a .pfx file containing the complete chain (the server cert, the servers private key as well as both the root and intermediate CA certs), tell kestrel to use this file, and then, when its not working, find out that the So, what is the actual plan to overcome this issue? Is there anything we could do to help get this sorted out? |
I agree with your thoughts about the importance of this scenario. It should "just work". As far as making progress on this issue, we need to understand what the capabilities/API of the underlying TLS stacks (SCHANNEL for Windows, OpenSsl for Linux, MacOS) are. At this point, we aren't sure the platforms support this functionality. |
I have almost exactly the same situation as @gingters . We would like to send the full chain if possible - and would also like to avoid placing root certificates in a user/computer-wide store for a variety of reasons. As mentioned, telling Kestrel to use a .pfx which provides a full chain and then having them conceptually "dropped on the floor" is not a good experience, and recent efforts (#31944) to be able to read formats that are often associated with carrying the full chain highlights this. (For anyone in the same situation who needs to have clients validate a certificate presented without its chain: if you can prime a list of potential certificates and get a grip on their chains beforehand, that's a good workaround. We can't, because the set of potential certificates is large and volatile. Our workaround right now is for the client to connect, receive the certificate, disconnect, use a secondary server or endpoint to retrieve the full chain and then connect again with the answer in hand. The alternative is to stall synchronously in the certificate validator, which is not only an incredibly bad idea but can also look hostile and malicious to the server, and is liable to be tripped up by defenses or timeouts in Kestrel or Schannel. Asynchronous certificate validation might have fixed this if this step wasn't supposed not to have huge pauses in it in the first place.) Schannel not supporting this is a reasonable explanation why, but considering alternate stacks can provide a solution it would be good to be able to opt into them. (And yes - I realize this is a big hammer, but it would also be able to pound in many nails.) |
Hi. We are having the exact same problem. Certificate trust chain is broken because only the server leaf certificate is sent without the intermediate CA (and root CA). The PFX file had all the certs bundled. Very unexpected and problematic behavior. As a workaround I am using HAProxy to terminate the SSL traffic and forward it to unencrypted port. |
the server part will be fixed in 5.0. There is now option to pass CertificateContext with all chain - perhaps loaded from pfx or PEM file. However, on Windows that will add intermediates to the store if needed - there is no other way how to deal with it as the handshake actually does not happen in the same process space. That was recommended by Windows platform developers. The client part will need some more work and thinking. It also seems like this morphed from client to server side back in 2019. Since this is probably too late for @cocowalla, I'm thinking about closing this and perhaps moving the server discussion to separate issue if needed - and the 5.0 behavior does not seem sufficient - #35844. |
Good to see some progress and that this issue is given some thought and attention. (Less good that the workaround is all that can happen on Windows, but that's not the fault of the .NET team.) |
Reassigning to System.Net.Security since it's about SslStream. The original issue was about client certs, and so far the SslCertificateContext type (which fixed it for the server role) isn't available to the client role. |
Tagging subscribers to this area: @dotnet/ncl, @vcsjones Issue DetailsI'm using the RabbitMQ C# Client, which under the hood uses Root CA -> Issuing CA -> Issued Client Certificate Using Wireshark I can see that when authenticating as a client, The certificate I'm using as the client cert is a PKCS#12 file that contains the whole chain (as
|
I think I am having the same issue here and so far (after days of trying) no solution for that. Our backend (which we can not touch) requires that two intermediate certificates are send along with the client certificate. In Wireshark we can observe, that only the client certificate is sent. When trying this with e.g. Python We are now thinking about switching technology since we can not come up with a proper solution for this. Does anyone have a workaround for this problem? It is a bit shocking, that something "simple" as:
seems to be impossible to achieve with .Net Core... This is the structure of our certificate:
|
The workaround is to install the client intermediate certificates into the Windows certificate store (I think the "My" store is the right place). |
Well, I noticed, that I can access my (mac OS) store using a X509Store. However, this is only my dev machine and we have to deploy this stuff to Microsoft Azure on Linux driven
So I was thinking to maybe add and remove the intermediate certificates on the fly when executing our tests (context: we have hundreds of virtual entities, that are alle equipped with client certificates. Therefore it would be cool not to "pollute" the systems too much by adding certificates to the store). Thank you @bartonjs for the hint with the |
This will probably be solved by #71194 in 8.0. Note that on Windows there is no API to pass specific certificates. |
Hi @bartonjs @rzikm I wrote the following code and tried to send it out. Should this be working? I still don't get the certificates sent with the request (no matter if I use
|
I do not know if this is the error, but you are adding the client certificate to the Certificate Authority store too. You may need to add it to the .My store. You may also need to Close/Dispose a store for the changes to take effect - I don't see anything confirming or denying this in the documentation. |
Hi @JesperTreetop , I updated the code above ^ , to now have two stores that are closed before sending the request. Still not working. Is is pretty hard to guess what is going on in the back when doing it like this. I was wondering how the client will know, that the intermediates belong to the client certificate added to |
In the standard X509 certificate chain-of-trust way. The client certificate is signed/issued by the intermediate certificate and the intermediate certificate is signed/issued by the root CA certificate. Each certificate also contains "issuer" metadata through which you can find an issuing certificate. See "Issuer" here, for example. Come to think of it, per the documentation, the intermediate certificate should go in the |
Hi @JesperTreetop, thank you for looking into this. I have now (again) updated the code above ^ and put the 3rd party root certificate into the
They form a valid chain, which I also checked using the |
I think maybe this is better answered by someone involved with that code. I'm just some dude guessing. It would be great if this just worked as intended out of the box. |
You should not need to add to You can add Last part is the certificate itself. Does it it have clients attributes e.g. proper KU/EKU? (#26531) as far as sending the intermediates: RFC for tls 1.2 states that client should send them. Without it, the server may have difficulty to construct the chain if the certificate some from different CAs. |
Hi @wfurt, many thanks for your feedback.
I am sorry if this caused any confusion. To me it looked like a related problem. Should I move this to a new thread? I tried to use the
The KU & EKU of the client certificate are:
|
You should not add the intermediates to |
Good day @wfurt, due to other urgent project tasks we postponed the implementation of this particular part. Now I am turning back to this and still was not able to figure out how to get this to work. I hope it is ok if we continue discussing this in this thread since we are developing on macOS and will deloy it on linux on Azure. To summarize what I need to achive: we need to send the leaf- and two intermediate certificates to the server (not the root certificate) for the server beeing able to trust the client. I was able to validate the chain using the following code:
However, I do not understand how to properly add / store the certificates, so that the I did not add those certificates to my system store (macOS Keychain) manually. This should also not be the goal, since the application is build to test a large sum of devices (clients) where each device comes with its own client certificate and might have differing intermediate certificates etc. So I tried using the
The result is always: I would be happy to discuss this in person if it is of any help. |
In 8.0, we have added support for Unfortunately, you can't provide the entire chain to SslClient/HttpClient in 7.0 (or earlier). You can provide only the (leaf) client certificate and SslStream will build the SslStreamCertificateContext internally (without specifying additional intermediates), so if the intermediates are somehow custom (i.e. not present in the system-wide intermediate certs stores), they will not be found and will not be sent over the wire. |
@rzikm that is a pitty. I guess, since 8.0 is currently in preview, it will take a while until it is supported by Azure Functions and therefore not usable (for us) as of now. Could you give an example of how Many thanks! EditWhen adding both intermediate and the root certificate to the macOS system truststore (Keychain) and trusting the root it is working (dotnet 6.0): |
yes, it should as long as X509Chain can find them. (watch for #80490 on macOS) runtime/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs Line 919 in 95df571
|
Hello again @wfurt , I had a closer look into the referenced tests and understand the following:
But how can this be used with our existing implementations where we use the I am on Update:I guess I have awnsered my own question by playing around with the code now. I don't know if there is a better way but if I use this approach, all certificates are sent and we have a successful response:
Thanks for your help here. Looking forward to dotnet v8 LTS :) |
Why is this still not fixed and we are forced to create different workarounds? Yesterday I've discovered that when you use SslServerAuthenticationOptions/SslStreamCertificateContext classes, certificates are being secretly added into Windows Certificate store without my consent. This is unacceptable and there's should be a way for developer to provide what he wants to send and it should just work without polluting random system stores... After 6 years Microsoft still not fixed such a simple bug... Unbelievable... |
feel free to contribute. This is open source project. This comes from limitation of the underlying schannel. If you know way how to do it please share it @tomrus88 |
Presumably the CA (Intermediates) store? Windows (the OS) does that when building a chain that a) it had to go find the intermediate and b) it ultimately trusted. That saves it the work of repeating "find it" in the future. The CA/Intermediates store implies no trust, the OS just treats it as a cache. |
I believe he means this particular piece of code. Lines 43 to 101 in 4bbde33
|
This is a very long discussion and I can't follow it entirely. I have an SslStream on the server side and need it to present a server certificate along with the CA certificate that signed it (both selfmade, in a local network). Did I understand it correctly that Windows 11 is uncapable of providing that? And .NET 8 relies on that Windows thing that's not suitable for the task? Can we please have OpenSSL in .NET on every platform if Windows doesn't support such basic scenarios? OpenVPN handles selfmade CA and other certificates effortlessly. So I assume that the Windows kernel isn't needed for SSL. (I'd even consider it dangerous to let the privileged kernel handle such complex data from the network!) |
This issue is quite old and there have been some changes made since it was filed. In currently supported versions of .NET, you can construct See specifically these two comments
There are many reasons why we can't use OpenSSL on Windows. Short version is that .NET tries very hard not to ship any cryptograpic code and relies on libraries ubiquitously present on the target platform. This is very much off-topic, but just to humor you I will list a few obstacles to relying on OpenSSL on windows:
|
Would something like this work with managed HttpClients? builder.Services.AddHttpClient("sample").UseSocketsHttpHandler((handler, sp) =>
{
handler.SslOptions = new SslClientAuthenticationOptions
{
// Pass in the mTLS cert and its chain here
ClientCertificateContext = SslStreamCertificateContext.Create(new X509Certificate2(), new X509Certificate2Collection())
};
}); |
Yes, this approach should work on all platforms and is the recommended way to pass certificates. Ideally also make sure the SslStreamCertificateContext object is getting reused as it's reuse removes some computation cost from the TLS handshake. I am not sure how many times the provided callback is invoked. |
From HttpClientFactory perspective, every time a new handler gets created, meaning at most every |
Perfect, thanks. Then I create a lazy factory for it and register the factory in the service provider. |
Note that the SslStreamCertificateContext puts certificates to store as needed as the fundamental limitation comes from underlying windows api |
I'm using the RabbitMQ C# Client, which under the hood uses
SslStream
. I'm having an issue where clients are unable to authenticate using x509 certificates if intermediate certificates are involved - such a chain looks like:Root CA -> Issuing CA -> Issued Client Certificate
Using Wireshark I can see that when authenticating as a client,
SslStream
is sending only the leaf certificate, which is causing a certificate handshake error. However, if the Root CA and Issuing CA are added to the Windows Certificate Store as trusted roots, thenSslStream
sends all 3 certificates, and RabbitMQ is happy.The certificate I'm using as the client cert is a PKCS#12 file that contains the whole chain (as
X509Certificate2
). So, the question is if there is any way to forceSslStream
to send the whole chain when authenticating as a client, even if the chain certs are not in the Windows Certificate Store?The text was updated successfully, but these errors were encountered: