Skip to content

Commit 56dd1c1

Browse files
authored
[Release/5.0] fix validation callback on Windows with Tls 1.3 (#55892)
* do not call RemoteCertificateValidationCallback on the same certificate again (#42836) * do not call RemoteCertificateValidationCallback on the same certificate agin * feedback from review * feedback from review * make sure we do not use Linq * adjust renegotiation tests to match product change (#43123) * adjust renegotiation tests to match product change * add assert for validationCount
1 parent 25a39c7 commit 56dd1c1

File tree

5 files changed

+58
-8
lines changed

5 files changed

+58
-8
lines changed

src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ public static IEnumerable<object[]> GetAsync_AllowedSSLVersion_Succeeds_MemberDa
100100
[MemberData(nameof(GetAsync_AllowedSSLVersion_Succeeds_MemberData))]
101101
public async Task GetAsync_AllowedSSLVersion_Succeeds(SslProtocols acceptedProtocol, bool requestOnlyThisProtocol)
102102
{
103+
int count = 0;
103104
using (HttpClientHandler handler = CreateHttpClientHandler())
104105
using (HttpClient client = CreateHttpClient(handler))
105106
{
106-
handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
107+
handler.ServerCertificateCustomValidationCallback =
108+
(request, cert, chain, errors) => { count++; return true; };
107109

108110
if (requestOnlyThisProtocol)
109111
{
@@ -131,6 +133,8 @@ await TestHelper.WhenAllCompletedOrAnyFailed(
131133
server.AcceptConnectionSendResponseAndCloseAsync(),
132134
client.GetAsync(url));
133135
}, options);
136+
137+
Assert.Equal(1, count);
134138
}
135139
}
136140

src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,17 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot
952952

953953
try
954954
{
955-
_remoteCertificate = CertificateValidationPal.GetRemoteCertificate(_securityContext, out remoteCertificateStore);
955+
X509Certificate2? certificate = CertificateValidationPal.GetRemoteCertificate(_securityContext, out remoteCertificateStore);
956+
957+
if (_remoteCertificate != null && certificate != null &&
958+
certificate.RawData.AsSpan().SequenceEqual(_remoteCertificate.RawData))
959+
{
960+
// This is renegotiation or TLS 1.3 and the certificate did not change.
961+
// There is no reason to process callback again as we already established trust.
962+
return true;
963+
}
964+
965+
_remoteCertificate = certificate;
956966

957967
if (_remoteCertificate == null)
958968
{

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,14 @@ public async Task SslStream_AllowRenegotiation_True_Succeeds()
5454
Assert.True(ssl.IsAuthenticated);
5555
Assert.True(ssl.IsEncrypted);
5656

57-
// Issue request that triggers regotiation from server.
57+
// Issue request that triggers renegotiation from server.
5858
byte[] message = Encoding.UTF8.GetBytes("GET /EchoClientCertificate.ashx HTTP/1.1\r\nHost: corefx-net-tls.azurewebsites.net\r\n\r\n");
5959
await ssl.WriteAsync(message, 0, message.Length);
6060

6161
// Initiate Read operation, that results in starting renegotiation as per server response to the above request.
6262
int bytesRead = await ssl.ReadAsync(message, 0, message.Length);
6363

64-
// Renegotiation will trigger another validation callback/
65-
Assert.InRange(validationCount, 2, int.MaxValue);
64+
Assert.Equal(1, validationCount);
6665
Assert.InRange(bytesRead, 1, message.Length);
6766
Assert.Contains("HTTP/1.1 200 OK", Encoding.UTF8.GetString(message));
6867
}

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,7 @@ public async Task SslStream_NetworkStream_Renegotiation_Succeeds(bool useSync)
220220
// Initiate Read operation, that results in starting renegotiation as per server response to the above request.
221221
int bytesRead = useSync ? ssl.Read(message, 0, message.Length) : await ssl.ReadAsync(message, 0, message.Length);
222222

223-
// renegotiation will trigger validation callback again.
224-
Assert.InRange(validationCount, 2, int.MaxValue);
223+
Assert.Equal(1, validationCount);
225224
Assert.InRange(bytesRead, 1, message.Length);
226225
Assert.Contains("HTTP/1.1 200 OK", Encoding.UTF8.GetString(message));
227226
}
@@ -249,6 +248,7 @@ public async Task SslStream_NestedAuth_Throws()
249248
public async Task SslStream_TargetHostName_Succeeds(bool useEmptyName)
250249
{
251250
string targetName = useEmptyName ? string.Empty : Guid.NewGuid().ToString("N");
251+
int count = 0;
252252

253253
(Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
254254
using (clientStream)
@@ -267,7 +267,7 @@ public async Task SslStream_TargetHostName_Succeeds(bool useEmptyName)
267267
{
268268
SslStream stream = (SslStream)sender;
269269
Assert.Equal(targetName, stream.TargetHostName);
270-
270+
count++;
271271
return true;
272272
};
273273

@@ -284,8 +284,12 @@ public async Task SslStream_TargetHostName_Succeeds(bool useEmptyName)
284284
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
285285
client.AuthenticateAsClientAsync(clientOptions),
286286
server.AuthenticateAsServerAsync(serverOptions));
287+
288+
await TestHelper.PingPong(client, server);
289+
287290
Assert.Equal(targetName, client.TargetHostName);
288291
Assert.Equal(targetName, server.TargetHostName);
292+
Assert.Equal(1, count);
289293
}
290294
}
291295

src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using System.Security.Cryptography.X509Certificates;
99
using System.Security.Cryptography.X509Certificates.Tests.Common;
1010
using System.Text;
11+
using System.Threading.Tasks;
12+
using Xunit;
1113

1214
namespace System.Net.Security.Tests
1315
{
@@ -37,6 +39,9 @@ public static class TestHelper
3739
private static readonly X509BasicConstraintsExtension s_eeConstraints =
3840
new X509BasicConstraintsExtension(false, false, 0, false);
3941

42+
private static readonly byte[] s_ping = Encoding.UTF8.GetBytes("PING");
43+
private static readonly byte[] s_pong = Encoding.UTF8.GetBytes("PONG");
44+
4045
public static (SslStream ClientStream, SslStream ServerStream) GetConnectedSslStreams()
4146
{
4247
(Stream clientStream, Stream serverStream) = GetConnectedStreams();
@@ -194,5 +199,33 @@ internal static (X509Certificate2 certificate, X509Certificate2Collection) Gener
194199

195200
return (endEntity, chain);
196201
}
202+
203+
internal static async Task PingPong(SslStream client, SslStream server)
204+
{
205+
byte[] buffer = new byte[s_ping.Length];
206+
ValueTask t = client.WriteAsync(s_ping);
207+
208+
int remains = s_ping.Length;
209+
while (remains > 0)
210+
{
211+
int readLength = await server.ReadAsync(buffer, buffer.Length - remains, remains);
212+
Assert.True(readLength > 0);
213+
remains -= readLength;
214+
}
215+
Assert.Equal(s_ping, buffer);
216+
await t;
217+
218+
t = server.WriteAsync(s_pong);
219+
remains = s_pong.Length;
220+
while (remains > 0)
221+
{
222+
int readLength = await client.ReadAsync(buffer, buffer.Length - remains, remains);
223+
Assert.True(readLength > 0);
224+
remains -= readLength;
225+
}
226+
227+
Assert.Equal(s_pong, buffer);
228+
await t;
229+
}
197230
}
198231
}

0 commit comments

Comments
 (0)