@@ -106,17 +106,19 @@ public Task ConnectWithRevocation_WithCallback(bool checkRevocation)
106
106
[ PlatformSpecific ( TestPlatforms . Linux ) ]
107
107
[ ConditionalTheory ]
108
108
[ OuterLoop ( "Subject to system load race conditions" ) ]
109
- [ InlineData ( false ) ]
110
- [ InlineData ( true ) ]
111
- public Task ConnectWithRevocation_StapledOcsp ( bool offlineContext )
109
+ [ InlineData ( false , false ) ]
110
+ [ InlineData ( true , false ) ]
111
+ [ InlineData ( false , true ) ]
112
+ [ InlineData ( true , true ) ]
113
+ public Task ConnectWithRevocation_StapledOcsp ( bool offlineContext , bool noIntermediates )
112
114
{
113
115
// Offline will only work if
114
116
// a) the revocation has been checked recently enough that it is cached, or
115
117
// b) the server stapled the response
116
118
//
117
119
// At high load, the server's background fetch might not have completed before
118
120
// this test runs.
119
- return ConnectWithRevocation_WithCallback_Core ( X509RevocationMode . Offline , offlineContext ) ;
121
+ return ConnectWithRevocation_WithCallback_Core ( X509RevocationMode . Offline , offlineContext , noIntermediates ) ;
120
122
}
121
123
122
124
[ Fact ]
@@ -192,7 +194,8 @@ static bool CertificateValidationCallback(
192
194
193
195
private async Task ConnectWithRevocation_WithCallback_Core (
194
196
X509RevocationMode revocationMode ,
195
- bool ? offlineContext = false )
197
+ bool ? offlineContext = false ,
198
+ bool noIntermediates = false )
196
199
{
197
200
string offlinePart = offlineContext . HasValue ? offlineContext . GetValueOrDefault ( ) . ToString ( ) . ToLower ( ) : "null" ;
198
201
string serverName = $ "{ revocationMode . ToString ( ) . ToLower ( ) } .{ offlinePart } .server.example";
@@ -203,13 +206,15 @@ private async Task ConnectWithRevocation_WithCallback_Core(
203
206
PkiOptions . EndEntityRevocationViaOcsp | PkiOptions . CrlEverywhere ,
204
207
out RevocationResponder responder ,
205
208
out CertificateAuthority rootAuthority ,
206
- out CertificateAuthority intermediateAuthority ,
209
+ out CertificateAuthority [ ] intermediateAuthorities ,
207
210
out X509Certificate2 serverCert ,
211
+ intermediateAuthorityCount : noIntermediates ? 0 : 1 ,
208
212
subjectName : serverName ,
209
213
keySize : 2048 ,
210
214
extensions : TestHelper . BuildTlsServerCertExtensions ( serverName ) ) ;
211
215
212
- X509Certificate2 issuerCert = intermediateAuthority . CloneIssuerCert ( ) ;
216
+ CertificateAuthority issuingAuthority = noIntermediates ? rootAuthority : intermediateAuthorities [ 0 ] ;
217
+ X509Certificate2 issuerCert = issuingAuthority . CloneIssuerCert ( ) ;
213
218
X509Certificate2 rootCert = rootAuthority . CloneIssuerCert ( ) ;
214
219
215
220
SslClientAuthenticationOptions clientOpts = new SslClientAuthenticationOptions
@@ -243,71 +248,80 @@ private async Task ConnectWithRevocation_WithCallback_Core(
243
248
serverCert = temp ;
244
249
}
245
250
246
- await using ( clientStream )
247
- await using ( serverStream )
248
- using ( responder )
249
- using ( rootAuthority )
250
- using ( intermediateAuthority )
251
- using ( serverCert )
252
- using ( issuerCert )
253
- using ( rootCert )
254
- await using ( SslStream tlsClient = new SslStream ( clientStream ) )
255
- await using ( SslStream tlsServer = new SslStream ( serverStream ) )
251
+ try
256
252
{
257
- intermediateAuthority . Revoke ( serverCert , serverCert . NotBefore ) ;
258
-
259
- SslServerAuthenticationOptions serverOpts = new SslServerAuthenticationOptions ( ) ;
260
-
261
- if ( offlineContext . HasValue )
253
+ await using ( clientStream )
254
+ await using ( serverStream )
255
+ using ( responder )
256
+ using ( rootAuthority )
257
+ using ( serverCert )
258
+ using ( issuerCert )
259
+ using ( rootCert )
260
+ await using ( SslStream tlsClient = new SslStream ( clientStream ) )
261
+ await using ( SslStream tlsServer = new SslStream ( serverStream ) )
262
262
{
263
- serverOpts . ServerCertificateContext = SslStreamCertificateContext . Create (
264
- serverCert ,
265
- new X509Certificate2Collection ( issuerCert ) ,
266
- offlineContext . GetValueOrDefault ( ) ) ;
263
+ issuingAuthority . Revoke ( serverCert , serverCert . NotBefore ) ;
267
264
268
- if ( revocationMode == X509RevocationMode . Offline )
265
+ SslServerAuthenticationOptions serverOpts = new SslServerAuthenticationOptions ( ) ;
266
+
267
+ if ( offlineContext . HasValue )
269
268
{
270
- if ( offlineContext . GetValueOrDefault ( false ) )
271
- {
272
- // Add a delay just to show we're not winning because of race conditions.
273
- await Task . Delay ( 200 ) ;
274
- }
275
- else
269
+ serverOpts . ServerCertificateContext = SslStreamCertificateContext . Create (
270
+ serverCert ,
271
+ new X509Certificate2Collection ( issuerCert ) ,
272
+ offlineContext . GetValueOrDefault ( ) ) ;
273
+
274
+ if ( revocationMode == X509RevocationMode . Offline )
276
275
{
277
- if ( ! OperatingSystem . IsLinux ( ) )
276
+ if ( offlineContext . GetValueOrDefault ( false ) )
278
277
{
279
- throw new InvalidOperationException (
280
- "This test configuration uses reflection and is only defined for Linux." ) ;
278
+ // Add a delay just to show we're not winning because of race conditions.
279
+ await Task . Delay ( 200 ) ;
281
280
}
282
-
283
- FieldInfo pendingDownloadTaskField = typeof ( SslStreamCertificateContext ) . GetField (
284
- "_pendingDownload" ,
285
- BindingFlags . Instance | BindingFlags . NonPublic ) ;
286
-
287
- if ( pendingDownloadTaskField is null )
281
+ else
288
282
{
289
- throw new InvalidOperationException ( "Cannot find the pending download field." ) ;
290
- }
291
-
292
- Task download = ( Task ) pendingDownloadTaskField . GetValue ( serverOpts . ServerCertificateContext ) ;
293
-
294
- // If it's null, it should mean it has already finished. If not, it might not have.
295
- if ( download is not null )
296
- {
297
- await download ;
283
+ if ( ! OperatingSystem . IsLinux ( ) )
284
+ {
285
+ throw new InvalidOperationException (
286
+ "This test configuration uses reflection and is only defined for Linux." ) ;
287
+ }
288
+
289
+ FieldInfo pendingDownloadTaskField = typeof ( SslStreamCertificateContext ) . GetField (
290
+ "_pendingDownload" ,
291
+ BindingFlags . Instance | BindingFlags . NonPublic ) ;
292
+
293
+ if ( pendingDownloadTaskField is null )
294
+ {
295
+ throw new InvalidOperationException ( "Cannot find the pending download field." ) ;
296
+ }
297
+
298
+ Task download = ( Task ) pendingDownloadTaskField . GetValue ( serverOpts . ServerCertificateContext ) ;
299
+
300
+ // If it's null, it should mean it has already finished. If not, it might not have.
301
+ if ( download is not null )
302
+ {
303
+ await download ;
304
+ }
298
305
}
299
306
}
300
307
}
308
+ else
309
+ {
310
+ serverOpts . ServerCertificate = serverCert ;
311
+ }
312
+
313
+ Task serverTask = tlsServer . AuthenticateAsServerAsync ( serverOpts ) ;
314
+ Task clientTask = tlsClient . AuthenticateAsClientAsync ( clientOpts ) ;
315
+
316
+ await TestConfiguration . WhenAllOrAnyFailedWithTimeout ( clientTask , serverTask ) ;
301
317
}
302
- else
318
+ }
319
+ finally
320
+ {
321
+ foreach ( CertificateAuthority intermediateAuthority in intermediateAuthorities )
303
322
{
304
- serverOpts . ServerCertificate = serverCert ;
323
+ intermediateAuthority . Dispose ( ) ;
305
324
}
306
-
307
- Task serverTask = tlsServer . AuthenticateAsServerAsync ( serverOpts ) ;
308
- Task clientTask = tlsClient . AuthenticateAsClientAsync ( clientOpts ) ;
309
-
310
- await TestConfiguration . WhenAllOrAnyFailedWithTimeout ( clientTask , serverTask ) ;
311
325
}
312
326
313
327
static bool CertificateValidationCallback (
0 commit comments