Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix SslStream client certificate credential caching - #132079

Merged
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug
Aug 12, 2026
Merged

Fix SslStream client certificate credential caching#132079
rzikm merged 4 commits into
dotnet:mainfrom
rzikm:tls-resume-test-bug

Conversation

@rzikm

@rzikmrzikm commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes#131992.

On Windows, SslStream intentionally starts mutual authentication with anonymous credentials when no matching client-certificate credential is cached. When the certificate was supplied through ClientCertificateContext, the selected certificate state was cleared but the context remained on the shared authentication options. The TlsSession/Schannel path could therefore acquire a certificate-bearing credential and cache it under the anonymous key, allowing a later connection without a client certificate to reuse it.

This change temporarily detaches ClientCertificateContext while acquiring the initial anonymous credential, then restores it immediately after credential acquisition completes.

A Windows-only RemoteExecutor regression test verifies that a connection using ClientCertificateContext does not pollute the anonymous credential cache for a subsequent connection with a different target host and no certificate. The test covers TLS 1.2 and TLS 1.3.

Local validation:

  • build.cmd clr+libs -rc release
  • System.Net.Security product build
  • New regression test: 2 passed
  • ClientChangeCert_NoResume: 3 passed
  • SslStream_NegotiateClientCertificateAsyncTls13_Succeeds: 2 passed
  • ServerAsyncAuthenticate_EachSupportedProtocol_Success: 2 passed

The full System.Net.Security functional suite ran 5,279 tests with one unrelated failure that reproduces in isolation: TlsSessionTests.ClientSession_ExternalCertificateValidation_AcceptWithDefaultValidation_FailsOnUntrustedCert (Expected: Not None, Actual: None).

Note

This pull request description was generated with GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts SslStream’s Windows client-credential acquisition flow to prevent a client-certificate credential (provided via ClientCertificateContext) from being cached under the “anonymous” credential cache key, and adds a Windows-only regression test to validate the behavior across TLS 1.2 and TLS 1.3.

Changes:

  • Temporarily detaches SslStreamCertificateContext while acquiring an initial anonymous credential and restores it when client credentials are requested (or on stream teardown).
  • Adds a Windows-only RemoteExecutor regression test to ensure a connection without a client certificate cannot reuse a previously cached client-certificate credential.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.csAdds a Windows-only regression test validating credential-cache isolation when using ClientCertificateContext.
src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.csDetaches/restores client CertificateContext during anonymous-credential acquisition to prevent cache pollution.
Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:87

  • Pass the protocol argument to RemoteExecutor using an invariant-culture conversion. If the parent process is running under a non-invariant culture, ((int)protocol).ToString() can emit native digits, which may not round-trip reliably in the child process.
 }, ((int)protocol).ToString()).DisposeAsync();

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9c29577e-370e-4808-a07c-9b0063e6a94a
CopilotAI review requested due to automatic review settings August 10, 2026 15:03

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings August 11, 2026 07:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs:56

  • The new RemoteExecutor-based regression test runs once per entry in SupportedSslProtocolsTestData (TLS 1.0/1.1/1.2/1.3, potentially SSL3 depending on platform settings). That increases test runtime and can introduce coverage over obsolete protocols that aren’t relevant to the reported caching issue (described as TLS 1.2/1.3). Consider narrowing the data source to TLS 1.2 and (when supported) TLS 1.3.
 [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_ClientCertificateContext_DoesNotPolluteAnonymousCredentialCache(SslProtocols protocol)

CopilotAI review requested due to automatic review settings August 11, 2026 11:41

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@rzikm
rzikm merged commit 49cb411 into dotnet:mainAug 12, 2026
76 of 79 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Net.Security TLS resumption and client-certificate tests fail together on Windows

3 participants

@rzikm@MihaZupan