-
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
Developers using Kestrel can configure the list of CAs per-hostname #45456
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsOur Service has the need to configure the certificate trust lists sent in the "Certificate Request" of the TLS handshake on a per-hostname basis. Currently, we configure this via Http.Sys APIs. We want to move to Kestrel, however SSLStream does not currently support this feature per my understanding. This issue was raised in #31097. # 1 and # 3 mentioned at the bottom of this issue were addressed in .NET 5.0. Unfortunately, # 2 was not addressed in .NET 5.0. Let's go through the specific details/scenarios: As a pre-requisite, the following registry key is set in order for the server to send Trusted Issuer List during TLS handshake. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\SendTrustedIssuerList is set to 1. Details : https://docs.microsoft.com/en-us/windows-server/security/tls/what-s-new-in-tls-ssl-schannel-ssp-overview Scenario 1: Mutual Auth with no defined CTL store. We configure a binding as follows: netsh http add sslcert hostnameport=pas.windows.net:443 certhash=EB3C3B94F10E948463929BCF4C7000C1E7BD0AC1 appid='{4dc3e181-e14b-4a21-b022-59fc669b0914}' certstorename=MY clientcertnegotiation=enable verifyclientcertrevocation=disable. To emphasize, I have not picked any particular CTL store for this binding. When a request comes in for the hostname pas.windows.net : during TLS handshake, in the "Certificate Request", the "Distinguished Names" is populated based on the trusted root of the server. (by default, since I did not pick a particular CTL store in the binding). Please look at frame 19 of the attached wireshark capture. (Transport Layer Security -> TLS 1.2 record layer ->HandShake Protocol: Certificate Request -> DistinguishedNames). The relevant snippet from frame 19 is below. The snippet shows that "Distinguished Names" is populated from trusted root on the server. Scenario 2: Mutual Auth: a defined CTL store with a non-empty list We configure a binding as follows: netsh http add sslcert hostnameport=device.login.microsoftonline.com:443 certhash=963B55D3E94101B70F1654FBF90D6006BAFAD513 appid='{4dc3e181-e14b-4a21-b022-59fc669b0914}' certstorename=MY clientcertnegotiation=enable verifyclientcertrevocation=disable sslctlstorename=DeviceLoginCTLStore To emphasize, I have picked a particular sslctlstore (DeviceLoginCTLStore) for this binding. When a request comes in for the hostname device.login.microsoftonline.com : during TLS handshake, in the "Certificate Request", the "Distinguished Names" list is populated from the CTL store specified in the binding (which is (DeviceLoginCTLStore)). Please look at frame 43 of the attached wireshark capture. (Transport Layer Security -> TLS 1.2 record layer ->HandShake Protocol: Certificate Request -> DistinguishedNames). The relevant snippet from frame 43 is below. You will see only one entry in the Distinguished Names because that particular store (DeviceLoginCTLStore) has only one entry. Scenario 3: Mutual Auth: a defined CTL store with a empty list We configure a binding as follows: netsh http add sslcert hostnameport=v2.aaddc.activedirectory.windowsazure.com:443 certhash=963B55D3E94101B70F1654FBF90D6006BAFAD513 appid='{4dc3e181-e14b-4a21-b022-59fc669b0914}' certstorename=MY clientcertnegotiation=enable verifyclientcertrevocation=disable sslctlstorename=EmptyCTLStore To emphasize, I have picked a particular sslctlstore (EmptyCTLStore) for this binding. EmptyCTLStore happens to be empty. When a request comes in for the hostname v2.aaddc.activedirectory.windowsazure.com : during TLS handshake, in the "Certificate Request", the "Distinguished Names" list is populated from the CTL store specified in the binding (which is (EmptyCTLStore)). Please look at frame 55 of the attached wireshark capture. (Transport Layer Security -> TLS 1.2 record layer ->HandShake Protocol: Certificate Request -> DistinguishedNames). The relevant snippet from frame 55 is below. You will see zero entries in the Distinguished Names because that particular store has no entries.
|
Or, if you're using an older version of .NET Core and just need to configure the cert itself per-hostname, you can use HttpsConnectionAdapterOptions.ServerCertificateSelector. |
netsh http commands were provided as an example of how they help configure Http.Sys, the ask is to provide a way to configure the same feature on Kestrel. The ServerOptionsSelectionCallback added in .NET 5.0 does not allow us to configure CTL stores per binding. |
We will need to probably extent SslServerAuthenticationOptions to make certificate request more configurable. |
It seems like this may be feasible. While on Windows, this seems to be tightly linked to X509Store via hRootStore in SCH_CREDENTIALS structure (server only), OpenSSL and macOS have way how to influence this well. SSL_CTX_set_client_CA_list and later SSL_CTX_set0_CA_list void SSL_CTX_set0_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list);
void SSL_set0_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list); macOS has SSLSetCertificateAuthorities
While linkage to X5609Store seems limiting as one cannot store this in database or other source, I'm not sure if we can make schannel to take simple list. Since we can iterate X509Store outside of windows, we can come up with list expected by the API. Since both X509Store and SslServerAuthenticationOptions are mutable, we would probably need to iterate for each SSL session. To limit the expense, we may choose to link this somehow to SslStreamCertificateContext as that is meant to be re-used over many SSL session so we could pay the expense only once when created. Since this may not be used commonly perhaps avoiding SslServerAuthenticationOptions may be beneficial as well. cc: @bartonjs @davidfowl @Tratcher for any additional thoughts. |
It seems like this will not be feasible on current Windows. After some digging, I discovered that Http.sys use SetCredentialsAttributes(SECPKG_ATTR_CLIENT_CERT_POLICY) and that call is available only in kernel mode and returns SEC_E_UNSUPPORTED_FUNCTION for us. There is alternative to specify custom CA trust. But that is additive e.g. feeding it with empty store would still behave like scenario 1. It seems like coming Co Windows release will have mechanism to do this but I don't know if it make sense to make new feature that does not function on all existing Windows versions. |
Just piling on here: Supporting custom trusted issuers lists through Kestrel is a feature that we're looking very much forward to as well for a product we're trying to ship. Is there any ETA at this time on when this could potentially land? |
What is your primary platform (if any) @dreijer? There are some technical difficulties and limits on Windows. |
There is no real Windows fix. more just a hack. That will still not give option to control it programmatically or selectively and it will be bound to system store. |
This should be available in preview7 on Windows. It currently depends on Windows 10 Insider preview. |
AB#1253385
Our Service has the need to configure the certificate trust lists sent in the "Certificate Request" of the TLS handshake on a per-hostname basis. Currently, we configure this via Http.Sys APIs.
We want to move to Kestrel, however SSLStream does not currently support this feature per my understanding.
This issue was raised in #31097. # 1 and # 3 mentioned at the bottom of that issue were addressed in .NET 5.0. Unfortunately, # 2 was not addressed in .NET 5.0.
Let's go through the specific details/scenarios:
As a pre-requisite, the following registry key is set in order for the server to send Trusted Issuer List during TLS handshake. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\SendTrustedIssuerList is set to 1. Details : https://docs.microsoft.com/en-us/windows-server/security/tls/what-s-new-in-tls-ssl-schannel-ssp-overview
Scenario 1: Mutual Auth with no defined CTL store.
We configure a binding as follows:
netsh http add sslcert hostnameport=pas.windows.net:443 certhash=EB3C3B94F10E948463929BCF4C7000C1E7BD0AC1 appid='{4dc3e181-e14b-4a21-b022-59fc669b0914}' certstorename=MY clientcertnegotiation=enable verifyclientcertrevocation=disable.
To emphasize, I have not picked any particular CTL store for this binding.
When a request comes in for the hostname pas.windows.net : during TLS handshake, in the "Certificate Request", the "Distinguished Names" is populated based on the trusted root of the server. (by default, since I did not pick a particular CTL store in the binding).
Please look at frame 19 of the attached wireshark capture. (Transport Layer Security -> TLS 1.2 record layer ->HandShake Protocol: Certificate Request -> DistinguishedNames).
The relevant snippet from frame 19 is below. The snippet shows that "Distinguished Names" is populated from trusted root on the server.
Below snippet shows the trusted root store on the machine: the "Distinguished Names" above are populated from the trusted root shown below.
Scenario 2: Mutual Auth: a defined CTL store with a non-empty list
We configure a binding as follows:
netsh http add sslcert hostnameport=device.login.microsoftonline.com:443 certhash=963B55D3E94101B70F1654FBF90D6006BAFAD513 appid='{4dc3e181-e14b-4a21-b022-59fc669b0914}' certstorename=MY clientcertnegotiation=enable verifyclientcertrevocation=disable sslctlstorename=DeviceLoginCTLStore
To emphasize, I have picked a particular sslctlstore (DeviceLoginCTLStore) for this binding.
When a request comes in for the hostname device.login.microsoftonline.com : during TLS handshake, in the "Certificate Request", the "Distinguished Names" list is populated from the CTL store specified in the binding (which is (DeviceLoginCTLStore)).
Please look at frame 43 of the attached wireshark capture. (Transport Layer Security -> TLS 1.2 record layer ->HandShake Protocol: Certificate Request -> DistinguishedNames).
The relevant snippet from frame 43 is below. You will see only one entry in the Distinguished Names because that particular store (DeviceLoginCTLStore) has only one entry.
Below snippet shows the DeviceLoginCTLStore on the server: the "Distinguished Names" above are populated from the DeviceLoginCTLStore below.
Scenario 3: Mutual Auth: a defined CTL store with a empty list
We configure a binding as follows:
netsh http add sslcert hostnameport=v2.aaddc.activedirectory.windowsazure.com:443 certhash=963B55D3E94101B70F1654FBF90D6006BAFAD513 appid='{4dc3e181-e14b-4a21-b022-59fc669b0914}' certstorename=MY clientcertnegotiation=enable verifyclientcertrevocation=disable sslctlstorename=EmptyCTLStore
To emphasize, I have picked a particular sslctlstore (EmptyCTLStore) for this binding. EmptyCTLStore happens to be empty.
When a request comes in for the hostname v2.aaddc.activedirectory.windowsazure.com : during TLS handshake, in the "Certificate Request", the "Distinguished Names" list is populated from the CTL store specified in the binding (which is (EmptyCTLStore)).
Please look at frame 55 of the attached wireshark capture. (Transport Layer Security -> TLS 1.2 record layer ->HandShake Protocol: Certificate Request -> DistinguishedNames).
The relevant snippet from frame 55 is below. You will see zero entries in the Distinguished Names because that particular store (EmptyCTLStore) has no entries.
Below snippet shows the EmptyCTLStore on the server:
serverhello.zip
The text was updated successfully, but these errors were encountered: