Description
openedon Feb 18, 2022
Description
We use SslStream
to connect with a device which allows to be configured for mutual authentication with client certificates. For that we use AuthenticateAsClientAsync
. After calling this method we check the IsMutuallyAuthenticated
property the way it is mentioned in the docs:
...Check the IsMutuallyAuthenticated property to determine whether mutual authentication occurred.
see Remarks section
This works fine so far. But subsequent connect with authenticate to the same device (server) now results in that the property IsMutuallyAuthenticated
returns true
even when this is not correct anymore.
This wrong behavior comes from the caching which is done by the SslStream as it is mentioned in the docs:
Note:
The Framework caches SSL sessions as they are created and attempts to reuse a cached session for a new request, if possible. When attempting to reuse an SSL session, the Framework uses the first element of ClientCertificates (if there is one), or tries to reuse an anonymous sessions if ClientCertificates is empty.
see Remarks section
Reproduction Steps
- Connect to a server via SslStream which requires mutual authentication via client certificate.
- Call
AuthenticateAsClientAsync
and provide the client certificate - Verify that
IsMutuallyAuthenticated
returns true - Disconnect
- Note: Keep the client running
- Restart and reconfigure the server so that it does not require a mutual authentication anymore
- Connect to a server via SslStream
- Call
AuthenticateAsClientAsync
and provide the client certificate - Verify what
IsMutuallyAuthenticated
returns now (see Expected and Actual behavior)
Expected behavior
In step 8:
8. IsMutuallyAuthenticated
should return false
Actual behavior
In step 8:
8. IsMutuallyAuthenticated
returns true
Although a TLS 1.2 connection was established without mutual authentication.
Regression?
I can reproduce this issue with:
- .NET 6
- .NET 5
- .NET Framework 4.8
Known Workarounds
This not-recommended workaround solves the issue:
- Call the following method before starting connecting to the server.
private static void ClearCache()
{
var sslAssembly = Assembly.GetAssembly(typeof(SslStream));
var sslSessionCacheClass = sslAssembly.GetType("System.Net.Security.SslSessionsCache");
var cachedCredsInfo = sslSessionCacheClass.GetField("s_cachedCreds", BindingFlags.NonPublic | BindingFlags.Static);
var cachedCreds = cachedCredsInfo.GetValue(null);
cachedCreds.GetType().GetMethod("Clear", BindingFlags.Public | BindingFlags.Instance).Invoke(cachedCreds, null);
}
Configuration
- Windows 10 20H2
Other information
Maybe this is related: #65539