@@ -16,9 +16,23 @@ internal sealed class TlsConnectionFeature : ITlsConnectionFeature, ITlsApplicat
1616{
1717 private readonly SslStream _sslStream ;
1818 private readonly ConnectionContext _context ;
19+ private bool _snapshotted ;
20+
1921 private X509Certificate2 ? _clientCert ;
2022 private Task < X509Certificate2 ? > ? _clientCertTask ;
2123
24+ private SslProtocols _protocol ;
25+ private TlsCipherSuite ? _negotiatedCipherSuite ;
26+ private ReadOnlyMemory < byte > _applicationProtocol ;
27+ #pragma warning disable SYSLIB0058 // Obsolete TLS cipher algorithm enums
28+ private CipherAlgorithmType _cipherAlgorithm ;
29+ private int _cipherStrength ;
30+ private HashAlgorithmType _hashAlgorithm ;
31+ private int _hashStrength ;
32+ private ExchangeAlgorithmType _keyExchangeAlgorithm ;
33+ private int _keyExchangeStrength ;
34+ #pragma warning restore SYSLIB0058
35+
2236 public TlsConnectionFeature ( SslStream sslStream , ConnectionContext context )
2337 {
2438 ArgumentNullException . ThrowIfNull ( sslStream ) ;
@@ -28,6 +42,47 @@ public TlsConnectionFeature(SslStream sslStream, ConnectionContext context)
2842 _context = context ;
2943 }
3044
45+ /// <summary>
46+ /// Captures all SslStream-backed property values so they remain accessible after the SslStream is disposed.
47+ /// Must be called before disposing the SslStream.
48+ /// </summary>
49+ internal void Snapshot ( )
50+ {
51+ if ( _snapshotted )
52+ {
53+ return ;
54+ }
55+ _snapshotted = true ;
56+
57+ if ( _sslStream is null )
58+ {
59+ return ;
60+ }
61+
62+ try
63+ {
64+ _protocol = _sslStream . SslProtocol ;
65+ _negotiatedCipherSuite = _sslStream . NegotiatedCipherSuite ;
66+ _applicationProtocol = _sslStream . NegotiatedApplicationProtocol . Protocol . ToArray ( ) ;
67+
68+ #pragma warning disable SYSLIB0058 // Obsolete TLS cipher algorithm enums
69+ _cipherAlgorithm = _sslStream . CipherAlgorithm ;
70+ _cipherStrength = _sslStream . CipherStrength ;
71+ _hashAlgorithm = _sslStream . HashAlgorithm ;
72+ _hashStrength = _sslStream . HashStrength ;
73+ _keyExchangeAlgorithm = _sslStream . KeyExchangeAlgorithm ;
74+ _keyExchangeStrength = _sslStream . KeyExchangeStrength ;
75+ #pragma warning restore SYSLIB0058
76+
77+ _clientCert ??= ConvertToX509Certificate2 ( _sslStream . RemoteCertificate ) ;
78+ }
79+ catch
80+ {
81+ // If the handshake never completed, SslStream properties may throw.
82+ // The snapshotted fields will retain their default values.
83+ }
84+ }
85+
3186 internal bool AllowDelayedClientCertificateNegotation { get ; set ; }
3287
3388 public X509Certificate2 ? ClientCertificate
@@ -45,33 +100,35 @@ public X509Certificate2? ClientCertificate
45100
46101 public string HostName { get ; set ; } = string . Empty ;
47102
48- public ReadOnlyMemory < byte > ApplicationProtocol => _sslStream . NegotiatedApplicationProtocol . Protocol ;
103+ public ReadOnlyMemory < byte > ApplicationProtocol => _snapshotted ? _applicationProtocol : _sslStream . NegotiatedApplicationProtocol . Protocol ;
49104
50- public SslProtocols Protocol => _sslStream . SslProtocol ;
105+ public SslProtocols Protocol => _snapshotted ? _protocol : _sslStream . SslProtocol ;
51106
52107 public SslStream SslStream => _sslStream ;
53108
54- // We don't store the values for these because they could be changed by a renegotiation.
109+ public Exception ? Exception { get ; set ; }
110+
111+ // After Snapshot() is called, all values are served from cached fields instead of the SslStream.
55112
56- public TlsCipherSuite ? NegotiatedCipherSuite => _sslStream . NegotiatedCipherSuite ;
113+ public TlsCipherSuite ? NegotiatedCipherSuite => _snapshotted ? _negotiatedCipherSuite : _sslStream . NegotiatedCipherSuite ;
57114
58115 [ Obsolete ( Obsoletions . RuntimeTlsCipherAlgorithmEnumsMessage , DiagnosticId = Obsoletions . RuntimeTlsCipherAlgorithmEnumsDiagId , UrlFormat = Obsoletions . RuntimeSharedUrlFormat ) ]
59- public CipherAlgorithmType CipherAlgorithm => _sslStream . CipherAlgorithm ;
116+ public CipherAlgorithmType CipherAlgorithm => _snapshotted ? _cipherAlgorithm : _sslStream . CipherAlgorithm ;
60117
61118 [ Obsolete ( Obsoletions . RuntimeTlsCipherAlgorithmEnumsMessage , DiagnosticId = Obsoletions . RuntimeTlsCipherAlgorithmEnumsDiagId , UrlFormat = Obsoletions . RuntimeSharedUrlFormat ) ]
62- public int CipherStrength => _sslStream . CipherStrength ;
119+ public int CipherStrength => _snapshotted ? _cipherStrength : _sslStream . CipherStrength ;
63120
64121 [ Obsolete ( Obsoletions . RuntimeTlsCipherAlgorithmEnumsMessage , DiagnosticId = Obsoletions . RuntimeTlsCipherAlgorithmEnumsDiagId , UrlFormat = Obsoletions . RuntimeSharedUrlFormat ) ]
65- public HashAlgorithmType HashAlgorithm => _sslStream . HashAlgorithm ;
122+ public HashAlgorithmType HashAlgorithm => _snapshotted ? _hashAlgorithm : _sslStream . HashAlgorithm ;
66123
67124 [ Obsolete ( Obsoletions . RuntimeTlsCipherAlgorithmEnumsMessage , DiagnosticId = Obsoletions . RuntimeTlsCipherAlgorithmEnumsDiagId , UrlFormat = Obsoletions . RuntimeSharedUrlFormat ) ]
68- public int HashStrength => _sslStream . HashStrength ;
125+ public int HashStrength => _snapshotted ? _hashStrength : _sslStream . HashStrength ;
69126
70127 [ Obsolete ( Obsoletions . RuntimeTlsCipherAlgorithmEnumsMessage , DiagnosticId = Obsoletions . RuntimeTlsCipherAlgorithmEnumsDiagId , UrlFormat = Obsoletions . RuntimeSharedUrlFormat ) ]
71- public ExchangeAlgorithmType KeyExchangeAlgorithm => _sslStream . KeyExchangeAlgorithm ;
128+ public ExchangeAlgorithmType KeyExchangeAlgorithm => _snapshotted ? _keyExchangeAlgorithm : _sslStream . KeyExchangeAlgorithm ;
72129
73130 [ Obsolete ( Obsoletions . RuntimeTlsCipherAlgorithmEnumsMessage , DiagnosticId = Obsoletions . RuntimeTlsCipherAlgorithmEnumsDiagId , UrlFormat = Obsoletions . RuntimeSharedUrlFormat ) ]
74- public int KeyExchangeStrength => _sslStream . KeyExchangeStrength ;
131+ public int KeyExchangeStrength => _snapshotted ? _keyExchangeStrength : _sslStream . KeyExchangeStrength ;
75132
76133 public Task < X509Certificate2 ? > GetClientCertificateAsync ( CancellationToken cancellationToken )
77134 {
0 commit comments