25
25
//
26
26
27
27
using System . Net ;
28
+ using System . Reflection ;
28
29
using System . Text ;
29
30
using System . Security . Cryptography . X509Certificates ;
30
31
@@ -79,7 +80,7 @@ public abstract class SecureMimeTestsBase
79
80
public const string ThunderbirdName = "[email protected] " ;
80
81
81
82
public static readonly string [ ] RelativeConfigFilePaths = {
82
- "certificate-authority.cfg" , "intermediate1.cfg" , "intermediate2.cfg" , "dnsnames/smime.cfg" , "dsa/smime.cfg" , "ec/smime.cfg" , "revoked/smime.cfg" , "rsa/smime.cfg"
83
+ "certificate-authority.cfg" , "intermediate1.cfg" , "intermediate2.cfg" , "dnsnames/smime.cfg" , "dsa/smime.cfg" , "ec/smime.cfg" , "nochain/smime.cfg" , " revoked/smime.cfg", "rsa/smime.cfg"
83
84
} ;
84
85
85
86
public static readonly string [ ] StartComCertificates = {
@@ -260,7 +261,7 @@ protected static Mock<HttpMessageHandler> CreateMockHttpMessageHandler ()
260
261
} ;
261
262
262
263
var mockHttpMessageHandler = new Mock < HttpMessageHandler > ( MockBehavior . Strict ) ;
263
-
264
+
264
265
for ( int i = 0 ; i < CrlRequestUris . Length ; i ++ ) {
265
266
var requestUri = CrlRequestUris [ i ] ;
266
267
var response = responses [ i ] ;
@@ -343,11 +344,11 @@ protected void ImportTestCertificates (SecureMimeContext ctx)
343
344
Assert . DoesNotThrow ( ( ) => ctx . Import ( mimekitCertificate . FileName , "no.secret" ) ) ;
344
345
}
345
346
346
- if ( windows is null ) {
347
- // Import the obsolete CRLs (we want the S/MIME context to download the current CRLs)
348
- foreach ( var crl in ObsoleteCrls )
349
- ctx . Import ( crl ) ;
350
- }
347
+ // if (windows is null) {
348
+ // // Import the obsolete CRLs (we want the S/MIME context to download the current CRLs)
349
+ // foreach (var crl in ObsoleteCrls)
350
+ // ctx.Import (crl);
351
+ // }
351
352
}
352
353
353
354
protected SecureMimeTestsBase ( )
@@ -3000,6 +3001,116 @@ protected async Task VerifyRevokedCertificateAsync (BouncyCastleSecureMimeContex
3000
3001
Assert . That ( innerException . Message , Does . EndWith ( ", reason: keyCompromise" ) ) ;
3001
3002
}
3002
3003
}
3004
+
3005
+ protected void VerifyCrlsResolvedWithBuildCertificateChain ( BouncyCastleSecureMimeContext ctx ,
3006
+ Mock < HttpMessageHandler > mockHttpMessageHandler )
3007
+ {
3008
+ var body = new TextPart ( "plain" ) { Text = "This is some cleartext that we'll end up signing..." } ;
3009
+ var certificate = SupportedCertificates . Single ( c
=> c . EmailAddress == "[email protected] " ) ;
3010
+
3011
+ var signer = new CmsSigner ( certificate . FileName , "no.secret" ) ;
3012
+ var multipart = MultipartSigned . Create ( ctx , signer , body ) ;
3013
+
3014
+ Assert . That ( multipart . Count , Is . EqualTo ( 2 ) , "The multipart/signed has an unexpected number of children." ) ;
3015
+
3016
+ var protocol = multipart . ContentType . Parameters [ "protocol" ] ;
3017
+ Assert . That ( protocol , Is . EqualTo ( ctx . SignatureProtocol ) , "The multipart/signed protocol does not match." ) ;
3018
+
3019
+ Assert . That ( multipart [ 0 ] , Is . InstanceOf < TextPart > ( ) , "The first child is not a text part." ) ;
3020
+ Assert . That ( multipart [ 1 ] , Is . InstanceOf < ApplicationPkcs7Signature > ( ) , "The second child is not a detached signature." ) ;
3021
+
3022
+ var signatures = multipart . Verify ( ctx ) ;
3023
+ Assert . That ( signatures . Count , Is . EqualTo ( 1 ) , "Verify returned an unexpected number of signatures." ) ;
3024
+
3025
+ AssertCrlsRequested ( mockHttpMessageHandler ) ;
3026
+
3027
+ AssertValidSignatures ( ctx , signatures ) ;
3028
+ }
3029
+
3030
+ protected void VerifyCrlsResolved ( BouncyCastleSecureMimeContext ctx ,
3031
+ Mock < HttpMessageHandler > mockHttpMessageHandler )
3032
+ {
3033
+ var body = new TextPart ( "plain" ) { Text = "This is some cleartext that we'll end up signing..." } ;
3034
+ var certificate = SupportedCertificates . Single ( c
=> c . EmailAddress == "[email protected] " ) ;
3035
+
3036
+ var signer = new CmsSigner ( certificate . FileName , "no.secret" ) ;
3037
+ var multipart = MultipartSigned . Create ( ctx , signer , body ) ;
3038
+
3039
+ Assert . That ( multipart . Count , Is . EqualTo ( 2 ) , "The multipart/signed has an unexpected number of children." ) ;
3040
+
3041
+ var protocol = multipart . ContentType . Parameters [ "protocol" ] ;
3042
+ Assert . That ( protocol , Is . EqualTo ( ctx . SignatureProtocol ) , "The multipart/signed protocol does not match." ) ;
3043
+
3044
+ Assert . That ( multipart [ 0 ] , Is . InstanceOf < TextPart > ( ) , "The first child is not a text part." ) ;
3045
+ Assert . That ( multipart [ 1 ] , Is . InstanceOf < ApplicationPkcs7Signature > ( ) , "The second child is not a detached signature." ) ;
3046
+
3047
+ var signatures = multipart . Verify ( ctx ) ;
3048
+ Assert . That ( signatures . Count , Is . EqualTo ( 1 ) , "Verify returned an unexpected number of signatures." ) ;
3049
+
3050
+ AssertCrlsRequested ( mockHttpMessageHandler ) ;
3051
+
3052
+ AssertValidSignatures ( ctx , signatures ) ;
3053
+ }
3054
+
3055
+ protected void VerifyCrlMissing ( BouncyCastleSecureMimeContext ctx , string errorContent )
3056
+ {
3057
+ var body = new TextPart ( "plain" ) { Text = "This is some cleartext that we'll end up signing..." } ;
3058
+ var certificate = SupportedCertificates . Single ( c
=> c . EmailAddress == "[email protected] " ) ;
3059
+
3060
+ var signer = new CmsSigner ( certificate . FileName , "no.secret" ) ;
3061
+ using var multipart = MultipartSigned . Create ( ctx , signer , body ) ;
3062
+
3063
+ Assert . That ( multipart . Count , Is . EqualTo ( 2 ) , "The multipart/signed has an unexpected number of children." ) ;
3064
+
3065
+ var protocol = multipart . ContentType . Parameters [ "protocol" ] ;
3066
+ Assert . That ( protocol , Is . EqualTo ( ctx . SignatureProtocol ) , "The multipart/signed protocol does not match." ) ;
3067
+
3068
+ Assert . That ( multipart [ 0 ] , Is . InstanceOf < TextPart > ( ) , "The first child is not a text part." ) ;
3069
+ Assert . That ( multipart [ 1 ] , Is . InstanceOf < ApplicationPkcs7Signature > ( ) , "The second child is not a detached signature." ) ;
3070
+
3071
+ var signatures = multipart . Verify ( ctx ) ;
3072
+ Assert . That ( signatures . Count , Is . EqualTo ( 1 ) , "Verify returned an unexpected number of signatures." ) ;
3073
+
3074
+ var ex = Assert . Throws < DigitalSignatureVerifyException > ( ( ) => signatures . Single ( ) . Verify ( ) ) ;
3075
+ Assert . That ( ex . Message , Does . StartWith ( "Failed to verify digital signature chain: Certification path could not be validated." ) ) ;
3076
+ Assert . That ( ex . InnerException ? . Message , Does . StartWith ( "Certification path could not be validated." ) ) ;
3077
+ Assert . That ( ex . InnerException ? . InnerException ? . Message , Is . EquivalentTo ( $ "No CRLs found for issuer \" { errorContent } \" ") ) ;
3078
+ }
3079
+
3080
+
3081
+ protected void VerifyMimeEncapsulatedSigningWithContext ( BouncyCastleSecureMimeContext ctx ,
3082
+ Mock < HttpMessageHandler > mockHttpMessageHandler )
3083
+ {
3084
+ var cleartext = new TextPart ( "plain" ) { Text = "This is some text that we'll end up signing..." } ;
3085
+
3086
+
3087
+ var certificate = SupportedCertificates . Single ( c
=> c . EmailAddress == "[email protected] " ) ;
3088
+
3089
+ var self = new MailboxAddress ( "MimeKit UnitTests" , certificate . EmailAddress ) ;
3090
+ var signed = ApplicationPkcs7Mime . Sign ( ctx , self , DigestAlgorithm . Sha1 , cleartext ) ;
3091
+ MimeEntity extracted ;
3092
+
3093
+ Assert . That ( signed . SecureMimeType , Is . EqualTo ( SecureMimeType . SignedData ) , "S/MIME type did not match." ) ;
3094
+
3095
+ var signatures = signed . Verify ( ctx , out extracted ) ;
3096
+
3097
+ Assert . That ( extracted , Is . InstanceOf < TextPart > ( ) , "Extracted part is not the expected type." ) ;
3098
+ Assert . That ( ( ( TextPart ) extracted ) . Text , Is . EqualTo ( cleartext . Text ) , "Extracted content is not the same as the original." ) ;
3099
+
3100
+ Assert . That ( signatures . Count , Is . EqualTo ( 1 ) , "Verify returned an unexpected number of signatures." ) ;
3101
+ AssertValidSignatures ( ctx , signatures ) ;
3102
+
3103
+ using ( var signedData = signed . Content . Open ( ) ) {
3104
+ using ( var stream = ctx . Verify ( signedData , out signatures ) )
3105
+ extracted = MimeEntity . Load ( stream ) ;
3106
+
3107
+ Assert . That ( extracted , Is . InstanceOf < TextPart > ( ) , "Extracted part is not the expected type." ) ;
3108
+ Assert . That ( ( ( TextPart ) extracted ) . Text , Is . EqualTo ( cleartext . Text ) , "Extracted content is not the same as the original." ) ;
3109
+
3110
+ Assert . That ( signatures . Count , Is . EqualTo ( 1 ) , "Verify returned an unexpected number of signatures." ) ;
3111
+ AssertValidSignatures ( ctx , signatures ) ;
3112
+ }
3113
+ }
3003
3114
}
3004
3115
3005
3116
[ TestFixture ]
@@ -3010,11 +3121,11 @@ class MyTemporarySecureMimeContext : TemporarySecureMimeContext
3010
3121
public readonly Mock < HttpMessageHandler > MockHttpMessageHandler ;
3011
3122
readonly HttpClient client ;
3012
3123
3013
- public MyTemporarySecureMimeContext ( ) : base ( new SecureRandom ( new CryptoApiRandomGenerator ( ) ) )
3124
+ public MyTemporarySecureMimeContext ( Mock < HttpMessageHandler > ? mockHttpMessageHandler = null ) : base ( new SecureRandom ( new CryptoApiRandomGenerator ( ) ) )
3014
3125
{
3015
3126
CheckCertificateRevocation = false ;
3016
3127
3017
- MockHttpMessageHandler = CreateMockHttpMessageHandler ( ) ;
3128
+ MockHttpMessageHandler = mockHttpMessageHandler ?? CreateMockHttpMessageHandler ( ) ;
3018
3129
client = new HttpClient ( MockHttpMessageHandler . Object ) ;
3019
3130
}
3020
3131
@@ -3055,6 +3166,92 @@ public async Task TestVerifyRevokedCertificateAsync ()
3055
3166
await VerifyRevokedCertificateAsync ( ctx , ctx . MockHttpMessageHandler ) ;
3056
3167
}
3057
3168
}
3169
+
3170
+ [ Test ]
3171
+ public void TestVerifyCrlsResolved ( )
3172
+ {
3173
+ using ( var ctx = new MyTemporarySecureMimeContext ( ) { CheckCertificateRevocation = true } ) {
3174
+ ImportTestCertificates ( ctx ) ;
3175
+
3176
+ VerifyCrlsResolved ( ctx , ctx . MockHttpMessageHandler ) ;
3177
+ }
3178
+ }
3179
+
3180
+ [ Test ]
3181
+ public void TestMissingRootCrl ( )
3182
+ {
3183
+ var responses = new HttpResponseMessage [ ]
3184
+ {
3185
+ new HttpResponseMessage ( HttpStatusCode . OK ) { Content = new ByteArrayContent ( CurrentCrls [ 1 ] . GetEncoded ( ) ) } ,
3186
+ new HttpResponseMessage ( HttpStatusCode . OK ) { Content = new ByteArrayContent ( CurrentCrls [ 2 ] . GetEncoded ( ) ) }
3187
+ } ;
3188
+ var crlUrlIndexes = new [ ] { 1 , 2 } ;
3189
+ var errorContent = RootCertificate . SubjectDN . ToString ( ) ;
3190
+
3191
+ VerifyCrlMissingTest ( responses , crlUrlIndexes , errorContent ) ;
3192
+ }
3193
+
3194
+ [ Test ]
3195
+ public void TestMissingPrimaryIntermediateCrl ( )
3196
+ {
3197
+ var responses = new HttpResponseMessage [ ]
3198
+ {
3199
+ new HttpResponseMessage ( HttpStatusCode . OK ) { Content = new ByteArrayContent ( CurrentCrls [ 0 ] . GetEncoded ( ) ) } ,
3200
+ new HttpResponseMessage ( HttpStatusCode . OK ) { Content = new ByteArrayContent ( CurrentCrls [ 2 ] . GetEncoded ( ) ) }
3201
+ } ;
3202
+ var crlUrlIndexes = new [ ] { 0 , 2 } ;
3203
+ var errorContent = IntermediateCertificate1 . SubjectDN . ToString ( ) ;
3204
+
3205
+ VerifyCrlMissingTest ( responses , crlUrlIndexes , errorContent ) ;
3206
+ }
3207
+
3208
+ [ Test ]
3209
+ public void TestMissingSecondaryIntermediateCrl ( )
3210
+ {
3211
+ var responses = new HttpResponseMessage [ ]
3212
+ {
3213
+ new HttpResponseMessage ( HttpStatusCode . OK ) { Content = new ByteArrayContent ( CurrentCrls [ 0 ] . GetEncoded ( ) ) } ,
3214
+ new HttpResponseMessage ( HttpStatusCode . OK ) { Content = new ByteArrayContent ( CurrentCrls [ 1 ] . GetEncoded ( ) ) }
3215
+ } ;
3216
+ var crlUrlIndexes = new [ ] { 0 , 1 } ;
3217
+ var errorContent = IntermediateCertificate2 . SubjectDN . ToString ( ) ;
3218
+
3219
+ VerifyCrlMissingTest ( responses , crlUrlIndexes , errorContent ) ;
3220
+ }
3221
+
3222
+ private void VerifyCrlMissingTest ( HttpResponseMessage [ ] responses , int [ ] crlUrlIndexes , string errorContent )
3223
+ {
3224
+ var mockHttpMessageHandler = new Mock < HttpMessageHandler > ( MockBehavior . Strict ) ;
3225
+
3226
+ for ( int i = 0 ; i < crlUrlIndexes . Length ; i ++ ) {
3227
+ var requestUri = CrlRequestUris [ crlUrlIndexes [ i ] ] ;
3228
+ var response = responses [ i ] ;
3229
+
3230
+ mockHttpMessageHandler
3231
+ . Protected ( )
3232
+ . Setup < Task < HttpResponseMessage > > (
3233
+ "SendAsync" ,
3234
+ ItExpr . Is < HttpRequestMessage > ( m => m . Method == HttpMethod . Get && m . RequestUri == requestUri ) ,
3235
+ ItExpr . IsAny < CancellationToken > ( ) )
3236
+ . ReturnsAsync ( response ) ;
3237
+ }
3238
+
3239
+ using ( var ctx = new MyTemporarySecureMimeContext ( mockHttpMessageHandler ) { CheckCertificateRevocation = true } ) {
3240
+ ImportTestCertificates ( ctx ) ;
3241
+
3242
+ VerifyCrlMissing ( ctx , errorContent ) ;
3243
+ }
3244
+ }
3245
+
3246
+ [ Test ]
3247
+ public virtual void TestVerifyCrlsResolvedWiithSecureMimeEncapsulatedSigningWithContext ( )
3248
+ {
3249
+ using ( var ctx = new MyTemporarySecureMimeContext ( ) { CheckCertificateRevocation = true } ) {
3250
+ ImportTestCertificates ( ctx ) ;
3251
+
3252
+ VerifyMimeEncapsulatedSigningWithContext ( ctx , ctx . MockHttpMessageHandler ) ;
3253
+ }
3254
+ }
3058
3255
}
3059
3256
3060
3257
[ TestFixture ]
0 commit comments