From f16687a0e964204aeb93c5747c43caab01073ba4 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 3 Jan 2025 12:28:15 -0500 Subject: [PATCH] Validate recipient certificates when encrypting via S/MIME Reverted logic that validated signing certificates. --- .../BouncyCastleSecureMimeContext.cs | 358 ++++++++---------- .../Cryptography/WindowsSecureMimeContext.cs | 23 +- .../Cryptography/ApplicationPkcs7MimeTests.cs | 307 ++++++++++++++- UnitTests/Cryptography/SecureMimeTests.cs | 238 +++--------- 4 files changed, 543 insertions(+), 383 deletions(-) diff --git a/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs b/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs index e5616f4085..a7836de1f4 100644 --- a/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs +++ b/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs @@ -330,15 +330,11 @@ CmsSignedDataStreamGenerator CreateSignedDataGenerator (CmsSigner signer) Stream Sign (CmsSigner signer, Stream content, bool encapsulate, CancellationToken cancellationToken) { - if (CheckCertificateRevocation) - ValidateCertificateChain (signer.CertificateChain, DateTime.UtcNow, cancellationToken); - var signedData = CreateSignedDataGenerator (signer); var memory = new MemoryBlockStream (); - using (var stream = signedData.Open (memory, encapsulate)) { + using (var stream = signedData.Open (memory, encapsulate)) content.CopyTo (stream, 4096); - } memory.Position = 0; @@ -347,15 +343,11 @@ Stream Sign (CmsSigner signer, Stream content, bool encapsulate, CancellationTok async Task SignAsync (CmsSigner signer, Stream content, bool encapsulate, CancellationToken cancellationToken) { - if (CheckCertificateRevocation) - await ValidateCertificateChainAsync (signer.CertificateChain, DateTime.UtcNow, cancellationToken).ConfigureAwait (false); - var signedData = CreateSignedDataGenerator (signer); var memory = new MemoryBlockStream (); - using (var stream = signedData.Open (memory, encapsulate)) { + using (var stream = signedData.Open (memory, encapsulate)) await content.CopyToAsync (stream, 4096, cancellationToken).ConfigureAwait (false); - } memory.Position = 0; @@ -741,156 +733,6 @@ protected IList BuildCertificateChain (X509Certificate certific return chain; } - /// - /// Validate an S/MIME certificate chain. - /// - /// - /// Validates an S/MIME certificate chain. - /// Downloads the CRLs for each certificate in the chain and then validates that the chain - /// is both valid and that none of the certificates in the chain have been revoked or compromised - /// in any way. - /// - /// true if the certificate chain is valid; otherwise, false. - /// The S/MIME certificate chain. - /// The date and time to use for validation. - /// The cancellation token. - /// - /// is . - /// - /// - /// is empty or contains a certificate. - /// - bool ValidateCertificateChain (X509CertificateChain chain, DateTime dateTime, CancellationToken cancellationToken = default) - { - if (chain == null) - throw new ArgumentNullException (nameof (chain)); - - if (chain.Count == 0) - throw new ArgumentException ("The certificate chain must contain at least one certificate.", nameof (chain)); - - if (chain.Any (certificate => certificate == null)) - throw new ArgumentException ("The certificate chain contains at least one null certificate.", nameof (chain)); - - var selector = new X509CertStoreSelector (); - - var userCertificateStore = new X509CertificateStore (); - userCertificateStore.AddRange (chain); - - var issuerStore = GetTrustedAnchors (); - var anchorStore = new X509CertificateStore (); - - foreach (var anchor in issuerStore) - anchorStore.Add (anchor.TrustedCert); - - var parameters = new PkixBuilderParameters (issuerStore, selector) { - ValidityModel = PkixParameters.PkixValidityModel, - IsRevocationEnabled = false, - Date = DateTime.UtcNow - }; - parameters.AddStoreCert (userCertificateStore); - - if (CheckCertificateRevocation) { - foreach (var certificate in chain) - DownloadCrls (certificate, cancellationToken); - } - - var intermediateStore = GetIntermediateCertificates (); - - foreach (var intermediate in intermediateStore.EnumerateMatches (MatchAllCertificates)) { - anchorStore.Add (intermediate); - if (CheckCertificateRevocation) - DownloadCrls (intermediate, cancellationToken); - } - - parameters.AddStoreCert (anchorStore); - - if (CheckCertificateRevocation) - parameters.AddStoreCrl (GetCertificateRevocationLists ()); - - try { - var builder = new PkixCertPathBuilder (); - builder.Build (parameters); - return true; - } catch { - return false; - } - } - - /// - /// Validate an S/MIME certificate chain. - /// - /// - /// Asynchronously validates an S/MIME certificate chain. - /// Downloads the CRLs for each certificate in the chain and then validates that the chain - /// is both valid and that none of the certificates in the chain have been revoked or compromised - /// in any way. - /// - /// true if the certificate chain is valid; otherwise, false. - /// The S/MIME certificate chain. - /// The date and time to use for validation. - /// The cancellation token. - /// - /// is . - /// - /// - /// is empty or contains a certificate. - /// - async Task ValidateCertificateChainAsync (X509CertificateChain chain, DateTime dateTime, CancellationToken cancellationToken = default) - { - if (chain == null) - throw new ArgumentNullException (nameof (chain)); - - if (chain.Count == 0) - throw new ArgumentException ("The certificate chain must contain at least one certificate.", nameof (chain)); - - if (chain.Any (certificate => certificate == null)) - throw new ArgumentException ("The certificate chain contains at least one null certificate.", nameof (chain)); - - var selector = new X509CertStoreSelector (); - - var userCertificateStore = new X509CertificateStore (); - userCertificateStore.AddRange (chain); - - var issuerStore = GetTrustedAnchors (); - var anchorStore = new X509CertificateStore (); - - foreach (var anchor in issuerStore) - anchorStore.Add (anchor.TrustedCert); - - var parameters = new PkixBuilderParameters (issuerStore, selector) { - ValidityModel = PkixParameters.PkixValidityModel, - IsRevocationEnabled = false, - Date = DateTime.UtcNow - }; - parameters.AddStoreCert (userCertificateStore); - - if (CheckCertificateRevocation) { - foreach (var certificate in chain) - await DownloadCrlsAsync (certificate, cancellationToken).ConfigureAwait (false); - } - - var intermediateStore = GetIntermediateCertificates (); - - foreach (var intermediate in intermediateStore.EnumerateMatches (MatchAllCertificates)) { - anchorStore.Add (intermediate); - if (CheckCertificateRevocation) - await DownloadCrlsAsync (intermediate, cancellationToken).ConfigureAwait (false); - } - - parameters.AddStoreCert (anchorStore); - - if (CheckCertificateRevocation) - parameters.AddStoreCrl (GetCertificateRevocationLists ()); - - try { - var builder = new PkixCertPathBuilder (); - builder.Build (parameters); - return true; - } catch { - return false; - } - } - PkixCertPath BuildCertPath (ISet anchors, IStore certificates, IStore crls, X509Certificate certificate, DateTime signingTime) { var selector = new X509CertStoreSelector { @@ -1646,39 +1488,108 @@ void CmsEnvelopeAddEllipticCurve (CmsEnvelopedDataGenerator cms, CmsRecipient re } } - Stream Envelope (CmsRecipientCollection recipients, Stream content, CancellationToken cancellationToken) + void AddRecipient (CmsEnvelopedDataGenerator cms, CmsRecipient recipient) { - var cms = new CmsEnvelopedDataGenerator (RandomNumberGenerator); - var unique = new HashSet (); - int count = 0; + var certificate = recipient.Certificate; + var pub = certificate.GetPublicKey (); + + if (pub is RsaKeyParameters) { + // Bouncy Castle dispatches OAEP based on the certificate type. However, MimeKit users + // expect to be able to specify the use of OAEP in S/MIME with certificates that have + // PKCS#1v1.5 OIDs as these tend to be more broadly compatible across the ecosystem. + // Thus, build our own RecipientInfoGenerator and register that for this key. + cms.AddRecipientInfoGenerator (new RsaOaepAwareRecipientInfoGenerator (recipient)); + } else if (pub is ECKeyParameters ellipticCurve) { + CmsEnvelopeAddEllipticCurve (cms, recipient, certificate, ellipticCurve); + } else { + var oid = certificate.SubjectPublicKeyInfo.Algorithm.Algorithm.ToString (); - foreach (var recipient in recipients) { - if (unique.Add (recipient.Certificate)) { - var certificate = recipient.Certificate; - var pub = certificate.GetPublicKey (); - - if (pub is RsaKeyParameters) { - // Bouncy Castle dispatches OAEP based on the certificate type. However, MimeKit users - // expect to be able to specify the use of OAEP in S/MIME with certificates that have - // PKCS#1v1.5 OIDs as these tend to be more broadly compatible across the ecosystem. - // Thus, build our own RecipientInfoGenerator and register that for this key. - cms.AddRecipientInfoGenerator (new RsaOaepAwareRecipientInfoGenerator (recipient)); - } else if (pub is ECKeyParameters ellipticCurve) { - CmsEnvelopeAddEllipticCurve (cms, recipient, certificate, ellipticCurve); - } else { - var oid = certificate.SubjectPublicKeyInfo.Algorithm.Algorithm.ToString (); - - throw new NotSupportedException ($"Unsupported type of recipient certificate: {pub.GetType ().Name} (SubjectPublicKeyInfo OID = {oid})"); - } + throw new NotSupportedException ($"Unsupported type of recipient certificate: {pub.GetType ().Name} (SubjectPublicKeyInfo OID = {oid})"); + } + } - count++; - } + void ValidateRecipientCertificate (X509Certificate certificate, CancellationToken cancellationToken = default) + { + DownloadCrls (certificate, cancellationToken); + + var selector = new X509CertStoreSelector () { + Certificate = certificate + }; + + var userCertificateStore = new X509CertificateStore (); + userCertificateStore.Add (certificate); + + var trustedAnchors = GetTrustedAnchors (); + var anchorStore = new X509CertificateStore (); + + foreach (var anchor in trustedAnchors) { + DownloadCrls (anchor.TrustedCert, cancellationToken); + anchorStore.Add (anchor.TrustedCert); } - if (count == 0) - throw new ArgumentException ("No recipients specified.", nameof (recipients)); + var intermediateStore = GetIntermediateCertificates (); - var algorithm = GetPreferredEncryptionAlgorithm (recipients); + foreach (var intermediate in intermediateStore.EnumerateMatches (MatchAllCertificates)) + DownloadCrls (intermediate, cancellationToken); + + var parameters = new PkixBuilderParameters (trustedAnchors, selector) { + ValidityModel = PkixParameters.PkixValidityModel, + IsRevocationEnabled = true, + Date = DateTime.UtcNow + }; + + parameters.AddStoreCert (userCertificateStore); + parameters.AddStoreCert (intermediateStore); + parameters.AddStoreCert (anchorStore); + + parameters.AddStoreCrl (GetCertificateRevocationLists ()); + + var builder = new PkixCertPathBuilder (); + builder.Build (parameters); + } + + async Task ValidateRecipientCertificateAsync (X509Certificate certificate, CancellationToken cancellationToken = default) + { + await DownloadCrlsAsync (certificate, cancellationToken).ConfigureAwait (false); + + var selector = new X509CertStoreSelector () { + Certificate = certificate + }; + + var userCertificateStore = new X509CertificateStore (); + userCertificateStore.Add (certificate); + + var trustedAnchors = GetTrustedAnchors (); + var anchorStore = new X509CertificateStore (); + + foreach (var anchor in trustedAnchors) { + await DownloadCrlsAsync (anchor.TrustedCert, cancellationToken).ConfigureAwait (false); + anchorStore.Add (anchor.TrustedCert); + } + + var intermediateStore = GetIntermediateCertificates (); + + foreach (var intermediate in intermediateStore.EnumerateMatches (MatchAllCertificates)) + await DownloadCrlsAsync (intermediate, cancellationToken).ConfigureAwait (false); + + var parameters = new PkixBuilderParameters (trustedAnchors, selector) { + ValidityModel = PkixParameters.PkixValidityModel, + IsRevocationEnabled = true, + Date = DateTime.UtcNow + }; + + parameters.AddStoreCert (userCertificateStore); + parameters.AddStoreCert (intermediateStore); + parameters.AddStoreCert (anchorStore); + + parameters.AddStoreCrl (GetCertificateRevocationLists ()); + + var builder = new PkixCertPathBuilder (); + builder.Build (parameters); + } + + Stream Envelope (CmsEnvelopedDataGenerator cms, EncryptionAlgorithm algorithm, Stream content, CancellationToken cancellationToken) + { var input = new CmsProcessableInputStream (content); CmsEnvelopedData envelopedData; @@ -1738,6 +1649,57 @@ Stream Envelope (CmsRecipientCollection recipients, Stream content, Cancellation return new MemoryStream (envelopedData.GetEncoded (), false); } + Stream Envelope (CmsRecipientCollection recipients, Stream content, CancellationToken cancellationToken) + { + var cms = new CmsEnvelopedDataGenerator (RandomNumberGenerator); + var unique = new HashSet (); + + foreach (var recipient in recipients) { + if (unique.Add (recipient.Certificate)) { + if (CheckCertificateRevocation) + ValidateRecipientCertificate (recipient.Certificate, cancellationToken); + + AddRecipient (cms, recipient); + } + } + + var algorithm = GetPreferredEncryptionAlgorithm (recipients); + + return Envelope (cms, algorithm, content, cancellationToken); + } + + async Task EnvelopeAsync (CmsRecipientCollection recipients, Stream content, CancellationToken cancellationToken) + { + var cms = new CmsEnvelopedDataGenerator (RandomNumberGenerator); + var unique = new HashSet (); + + foreach (var recipient in recipients) { + if (unique.Add (recipient.Certificate)) { + if (CheckCertificateRevocation) + await ValidateRecipientCertificateAsync (recipient.Certificate, cancellationToken).ConfigureAwait (false); + + AddRecipient (cms, recipient); + } + } + + var algorithm = GetPreferredEncryptionAlgorithm (recipients); + + // Note: BouncyCastle's CmsEnvelopedDataGenerator does not support async operations. + // + // If the content isn't already a memory stream of some sort, we clone it into a memory stream + // in order to provide asynchronous reading from the source content stream. + if (content is MemoryBlockStream or MemoryStream) + return Envelope (cms, algorithm, content, cancellationToken); + + using (var memory = new MemoryBlockStream ()) { + await content.CopyToAsync (memory, 4096, cancellationToken).ConfigureAwait (false); + + memory.Position = 0; + + return Envelope (cms, algorithm, memory, cancellationToken); + } + } + /// /// Encrypt the specified content for the specified recipients. /// @@ -1754,6 +1716,9 @@ Stream Envelope (CmsRecipientCollection recipients, Stream content, Cancellation /// -or- /// is . /// + /// + /// is empty. + /// /// /// The operation was canceled via the cancellation token. /// @@ -1765,6 +1730,9 @@ public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, if (recipients == null) throw new ArgumentNullException (nameof (recipients)); + if (recipients.Count == 0) + throw new ArgumentException ("No recipients specified.", nameof (recipients)); + if (content == null) throw new ArgumentNullException (nameof (content)); @@ -1789,6 +1757,9 @@ public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, /// -or- /// is . /// + /// + /// is empty. + /// /// /// The operation was canceled via the cancellation token. /// @@ -1800,18 +1771,13 @@ public override async Task EncryptAsync (CmsRecipientColle if (recipients == null) throw new ArgumentNullException (nameof (recipients)); + if (recipients.Count == 0) + throw new ArgumentException ("No recipients specified.", nameof (recipients)); + if (content == null) throw new ArgumentNullException (nameof (content)); - Stream envelopedData; - - using (var memory = new MemoryBlockStream ()) { - await content.CopyToAsync (memory, 4096, cancellationToken).ConfigureAwait (false); - - memory.Position = 0; - - envelopedData = Envelope (recipients, memory, cancellationToken); - } + var envelopedData = await EnvelopeAsync (recipients, content, cancellationToken).ConfigureAwait (false); return new ApplicationPkcs7Mime (SecureMimeType.EnvelopedData, envelopedData); } diff --git a/MimeKit/Cryptography/WindowsSecureMimeContext.cs b/MimeKit/Cryptography/WindowsSecureMimeContext.cs index 9da0060f2c..0eca2aa4d1 100644 --- a/MimeKit/Cryptography/WindowsSecureMimeContext.cs +++ b/MimeKit/Cryptography/WindowsSecureMimeContext.cs @@ -265,6 +265,9 @@ protected virtual RealCmsRecipient GetCmsRecipient (MailboxAddress mailbox) /// /// is . /// + /// + /// is empty. + /// /// /// A certificate for one or more of the specified could not be found. /// @@ -1222,6 +1225,9 @@ Task EnvelopeAsync (CmsRecipientCollection recipients, Stream content, C /// -or- /// is . /// + /// + /// is empty. + /// /// /// The operation was canceled via the cancellation token. /// @@ -1233,6 +1239,9 @@ public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, if (recipients == null) throw new ArgumentNullException (nameof (recipients)); + if (recipients.Count == 0) + throw new ArgumentException ("No recipients specified.", nameof (recipients)); + if (content == null) throw new ArgumentNullException (nameof (content)); @@ -1257,6 +1266,9 @@ public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, /// -or- /// is . /// + /// + /// is empty. + /// /// /// The operation was canceled via the cancellation token. /// @@ -1268,6 +1280,9 @@ public override async Task EncryptAsync (CmsRecipientColle if (recipients == null) throw new ArgumentNullException (nameof (recipients)); + if (recipients.Count == 0) + throw new ArgumentException ("No recipients specified.", nameof (recipients)); + if (content == null) throw new ArgumentNullException (nameof (content)); @@ -1293,7 +1308,9 @@ public override async Task EncryptAsync (CmsRecipientColle /// is . /// /// - /// A certificate for one or more of the could not be found. + /// A certificate for one or more of the could not be found. + /// -or- + /// No recipients were specified. /// /// /// The operation was canceled via the cancellation token. @@ -1335,7 +1352,9 @@ public override MimePart Encrypt (IEnumerable recipients, Stream /// is . /// /// - /// A certificate for one or more of the could not be found. + /// A certificate for one or more of the could not be found. + /// -or- + /// No recipients were specified. /// /// /// The operation was canceled via the cancellation token. diff --git a/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs b/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs index e695b859d4..93a7c003f5 100644 --- a/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs +++ b/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs @@ -24,13 +24,18 @@ // THE SOFTWARE. // +using System.Net; using System.Text; using System.Security.Cryptography.X509Certificates; +using Org.BouncyCastle.Pkix; using Org.BouncyCastle.X509; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Prng; +using Moq; +using Moq.Protected; + using MimeKit; using MimeKit.Cryptography; @@ -245,7 +250,57 @@ public void TestSecureMimeTypes () } } - static void ImportAll (SecureMimeContext ctx) + protected static HttpResponseMessage[] RevokedCertificateResponses () + { + return new HttpResponseMessage[] { + new HttpResponseMessage (HttpStatusCode.OK) { + Content = new ByteArrayContent (SecureMimeTestsBase.CurrentCrls[0].GetEncoded ()) + }, + new HttpResponseMessage (HttpStatusCode.OK) { + Content = new ByteArrayContent (SecureMimeTestsBase.CurrentCrls[1].GetEncoded ()) + }, + new HttpResponseMessage (HttpStatusCode.OK) { + Content = new ByteArrayContent (SecureMimeTestsBase.CurrentCrls[2].GetEncoded ()) + } + }; + } + + protected static HttpResponseMessage[] RevokedNoChainCertificateResponses () + { + return new HttpResponseMessage[] { + new HttpResponseMessage (HttpStatusCode.OK) { + Content = new ByteArrayContent (SecureMimeTestsBase.CurrentCrls[0].GetEncoded ()) + }, + new HttpResponseMessage (HttpStatusCode.OK) { + Content = new ByteArrayContent (SecureMimeTestsBase.CurrentCrls[1].GetEncoded ()) + }, + new HttpResponseMessage (HttpStatusCode.OK) { + Content = new ByteArrayContent (SecureMimeTestsBase.CurrentCrls[3].GetEncoded ()) + } + }; + } + + protected static Mock CreateMockHttpMessageHandler (HttpResponseMessage[] responses) + { + var mockHttpMessageHandler = new Mock (MockBehavior.Strict); + + for (int i = 0; i < SecureMimeTestsBase.CrlRequestUris.Length; i++) { + var requestUri = SecureMimeTestsBase.CrlRequestUris[i]; + var response = responses[i]; + + mockHttpMessageHandler + .Protected () + .Setup> ( + "SendAsync", + ItExpr.Is (m => m.Method == HttpMethod.Get && m.RequestUri == requestUri), + ItExpr.IsAny ()) + .ReturnsAsync (response); + } + + return mockHttpMessageHandler; + } + + protected static void ImportAll (SecureMimeContext ctx) { var dataDir = Path.Combine (TestHelper.ProjectDir, "TestData", "smime"); var windows = ctx as WindowsSecureMimeContext; @@ -303,7 +358,7 @@ static void ImportAll (SecureMimeContext ctx) } } - static async Task ImportAllAsync (SecureMimeContext ctx) + protected static async Task ImportAllAsync (SecureMimeContext ctx) { var dataDir = Path.Combine (TestHelper.ProjectDir, "TestData", "smime"); var windows = ctx as WindowsSecureMimeContext; @@ -922,12 +977,104 @@ public async Task TestSignAndEncryptDnsNamesAsync () } } } + + protected static void VerifyRevokedRecipient (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler, SMimeCertificate certificate, bool validateEncrypt) + { + var body = new TextPart ("plain") { Text = "This is the message body that we will be encrypting..." }; + var recipients = new CmsRecipientCollection () { + new CmsRecipient (certificate.Certificate) + }; + + ctx.CheckCertificateRevocation = validateEncrypt; + + if (validateEncrypt) { + try { + ApplicationPkcs7Mime.Encrypt (ctx, recipients, body); + Assert.Fail ("Encrypt should have thrown an exception for the revoked recipient certificate."); + } catch (Exception ex) { + Assert.That (ex.Message, Is.EqualTo ("Certification path could not be validated."), "ex.Message"); + + Exception innerException = ex.InnerException; + while (innerException is not PkixCertPathValidatorException && innerException.InnerException != null) + innerException = innerException.InnerException; + + Assert.That (innerException, Is.Not.Null); + Assert.That (innerException.Message, Does.StartWith ("Certificate revocation after ")); + Assert.That (innerException.Message, Does.EndWith (", reason: keyCompromise")); + } finally { + ctx.CheckCertificateRevocation = false; + } + + SecureMimeTestsBase.AssertCrlsRequested (mockHttpMessageHandler); + } else { + Assert.DoesNotThrow (() => ApplicationPkcs7Mime.Encrypt (ctx, recipients, body)); + SecureMimeTestsBase.AssertCrlsNotRequested (mockHttpMessageHandler); + } + } + + protected static async Task VerifyRevokedRecipientAsync (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler, SMimeCertificate certificate, bool validateEncrypt) + { + var body = new TextPart ("plain") { Text = "This is the message body that we will be encrypting..." }; + var recipients = new CmsRecipientCollection () { + new CmsRecipient (certificate.Certificate) + }; + + ctx.CheckCertificateRevocation = validateEncrypt; + + if (validateEncrypt) { + try { + await ApplicationPkcs7Mime.EncryptAsync (ctx, recipients, body); + Assert.Fail ("EncryptAsync should have thrown an exception for the revoked recipient certificate."); + } catch (Exception ex) { + Assert.That (ex.Message, Is.EqualTo ("Certification path could not be validated."), "ex.Message"); + + Exception innerException = ex.InnerException; + while (innerException is not PkixCertPathValidatorException && innerException.InnerException != null) + innerException = innerException.InnerException; + + Assert.That (innerException, Is.Not.Null); + Assert.That (innerException.Message, Does.StartWith ("Certificate revocation after ")); + Assert.That (innerException.Message, Does.EndWith (", reason: keyCompromise")); + } finally { + ctx.CheckCertificateRevocation = false; + } + + SecureMimeTestsBase.AssertCrlsRequested (mockHttpMessageHandler); + } else { + Assert.DoesNotThrow (() => ApplicationPkcs7Mime.Encrypt (ctx, recipients, body)); + SecureMimeTestsBase.AssertCrlsNotRequested (mockHttpMessageHandler); + } + } } [TestFixture] public class ApplicationPkcs7MimeTests : ApplicationPkcs7MimeTestsBase { - readonly TemporarySecureMimeContext ctx = new TemporarySecureMimeContext (new SecureRandom (new CryptoApiRandomGenerator ())) { CheckCertificateRevocation = false }; + class MyTemporarySecureMimeContext : TemporarySecureMimeContext + { + public readonly Mock MockHttpMessageHandler; + readonly HttpClient client; + + public MyTemporarySecureMimeContext (Mock? mockHttpMessageHandler = null) : base (new SecureRandom (new CryptoApiRandomGenerator ())) + { + CheckCertificateRevocation = false; + + MockHttpMessageHandler = mockHttpMessageHandler ?? CreateMockHttpMessageHandler (RevokedCertificateResponses ()); + client = new HttpClient (MockHttpMessageHandler.Object); + } + + protected override HttpClient HttpClient { + get { return client; } + } + } + + readonly TemporarySecureMimeContext ctx = new MyTemporarySecureMimeContext (); + + [OneTimeTearDown] + public void Cleanup () + { + ctx.Dispose (); + } public ApplicationPkcs7MimeTests () { @@ -938,6 +1085,62 @@ protected override SecureMimeContext CreateContext () { return ctx; } + + [Test] + public void TestEncryptRevokedRecipient () + { + using (var ctx = new MyTemporarySecureMimeContext ()) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, false); + } + + using (var ctx = new MyTemporarySecureMimeContext ()) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, true); + } + + using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, false); + } + + using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, true); + } + } + + [Test] + public async Task TestEncryptRevokedRecipientAsync () + { + using (var ctx = new MyTemporarySecureMimeContext ()) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, false); + } + + using (var ctx = new MyTemporarySecureMimeContext ()) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, true); + } + + using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, false); + } + + using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, true); + } + } } [TestFixture] @@ -945,9 +1148,23 @@ public class ApplicationPkcs7MimeSqliteTests : ApplicationPkcs7MimeTestsBase { class MySecureMimeContext : DefaultSecureMimeContext { - public MySecureMimeContext () : base ("pkcs7.db", "no.secret") + public readonly Mock MockHttpMessageHandler; + readonly HttpClient client; + + public MySecureMimeContext () : this ("pkcs7.db", "no.secret") + { + } + + public MySecureMimeContext (string database, string password, Mock? mockHttpMessageHandler = null) : base (database, password) { CheckCertificateRevocation = false; + + MockHttpMessageHandler = mockHttpMessageHandler ?? CreateMockHttpMessageHandler (RevokedCertificateResponses ()); + client = new HttpClient (MockHttpMessageHandler.Object); + } + + protected override HttpClient HttpClient { + get { return client; } } } @@ -965,6 +1182,88 @@ static ApplicationPkcs7MimeSqliteTests () { if (File.Exists ("pkcs7.db")) File.Delete ("pkcs7.db"); + + if (File.Exists ("revoked-pkcs7.db")) + File.Delete ("revoked-pkcs7.db"); + + if (File.Exists ("revoked-pkcs7-async.db")) + File.Delete ("revoked-pkcs7-async.db"); + } + + [Test] + public void TestEncryptRevokedRecipient () + { + const string fileName = "revoked-pkcs7.db"; + + using (var ctx = new MySecureMimeContext (fileName, "no.secret")) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, false); + } + + File.Delete (fileName); + + using (var ctx = new MySecureMimeContext (fileName, "no.secret")) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, true); + } + + File.Delete (fileName); + + using (var ctx = new MySecureMimeContext (fileName, "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, false); + } + + File.Delete (fileName); + + using (var ctx = new MySecureMimeContext (fileName, "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + ImportAll (ctx); + + VerifyRevokedRecipient (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, true); + } + + File.Delete (fileName); + } + + [Test] + public async Task TestEncryptRevokedRecipientAsync () + { + const string fileName = "revoked-pkcs7-async.db"; + + using (var ctx = new MySecureMimeContext (fileName, "no.secret")) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, false); + } + + File.Delete (fileName); + + using (var ctx = new MySecureMimeContext (fileName, "no.secret")) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedCertificate, true); + } + + File.Delete (fileName); + + using (var ctx = new MySecureMimeContext (fileName, "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, false); + } + + File.Delete (fileName); + + using (var ctx = new MySecureMimeContext (fileName, "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + await ImportAllAsync (ctx); + + await VerifyRevokedRecipientAsync (ctx, ctx.MockHttpMessageHandler, SecureMimeTestsBase.RevokedNoChainCertificate, true); + } + + File.Delete (fileName); } } } diff --git a/UnitTests/Cryptography/SecureMimeTests.cs b/UnitTests/Cryptography/SecureMimeTests.cs index 4a0f4de638..cf2abc1150 100644 --- a/UnitTests/Cryptography/SecureMimeTests.cs +++ b/UnitTests/Cryptography/SecureMimeTests.cs @@ -25,7 +25,6 @@ // using System.Net; -using System.Reflection; using System.Text; using System.Security.Cryptography.X509Certificates; @@ -369,11 +368,11 @@ protected void ImportTestCertificates (SecureMimeContext ctx) Assert.DoesNotThrow (() => ctx.Import (mimekitCertificate.FileName, "no.secret")); } - // if (windows is null) { - // // Import the obsolete CRLs (we want the S/MIME context to download the current CRLs) - // foreach (var crl in ObsoleteCrls) - // ctx.Import (crl); - // } + if (windows is null) { + // Import the obsolete CRLs (we want the S/MIME context to download the current CRLs) + foreach (var crl in ObsoleteCrls) + ctx.Import (crl); + } } protected SecureMimeTestsBase () @@ -447,10 +446,13 @@ public void TestArgumentExceptions () using (var ctx = CreateContext ()) { var signer = new CmsSigner (Path.Combine (TestHelper.ProjectDir, "TestData", "smime", "rsa", "smime.pfx"), "no.secret"); var mailbox = new MailboxAddress ("Unit Tests", "example@mimekit.net"); + var emptyRecipients = new CmsRecipientCollection (); var recipients = new CmsRecipientCollection (); DigitalSignatureCollection signatures; MimeEntity entity; + recipients.Add (new CmsRecipient (RsaCertificate.Certificate)); + Assert.That (ctx.Supports ("text/plain"), Is.False, "Should not support text/plain"); Assert.That (ctx.Supports ("application/octet-stream"), Is.False, "Should not support application/octet-stream"); Assert.That (ctx.Supports ("application/pkcs7-mime"), Is.True, "Should support application/pkcs7-mime"); @@ -487,11 +489,13 @@ public void TestArgumentExceptions () Assert.ThrowsAsync (() => ctx.EncapsulatedSignAsync (mailbox, DigestAlgorithm.Sha256, null)); Assert.Throws (() => ctx.Encrypt ((CmsRecipientCollection) null, stream)); Assert.Throws (() => ctx.Encrypt (recipients, null)); + Assert.Throws (() => ctx.Encrypt (emptyRecipients, stream)); Assert.Throws (() => ctx.Encrypt ((IEnumerable) null, stream)); Assert.Throws (() => ctx.Encrypt (Array.Empty (), null)); Assert.Throws (() => ctx.Encrypt (Array.Empty (), stream)); Assert.ThrowsAsync (() => ctx.EncryptAsync ((CmsRecipientCollection) null, stream)); Assert.ThrowsAsync (() => ctx.EncryptAsync (recipients, null)); + Assert.ThrowsAsync (() => ctx.EncryptAsync (emptyRecipients, stream)); Assert.ThrowsAsync (() => ctx.EncryptAsync ((IEnumerable) null, stream)); Assert.ThrowsAsync (() => ctx.EncryptAsync (Array.Empty (), null)); Assert.ThrowsAsync (() => ctx.EncryptAsync (Array.Empty (), stream)); @@ -2872,7 +2876,7 @@ public async Task TestSecureMimeVerifyMixedLineEndingsAsync () } } - void AssertCrlsRequested (Mock mockHttpMessageHandler) + public static void AssertCrlsRequested (Mock mockHttpMessageHandler) { try { for (int i = 0; i < CrlRequestUris.Length; i++) { @@ -2889,7 +2893,7 @@ void AssertCrlsRequested (Mock mockHttpMessageHandler) } } - void AssertCrlsNotRequested (Mock mockHttpMessageHandler) + public static void AssertCrlsNotRequested (Mock mockHttpMessageHandler) { try { for (int i = 0; i < CrlRequestUris.Length; i++) { @@ -2906,19 +2910,11 @@ void AssertCrlsNotRequested (Mock mockHttpMessageHandler) } } - protected void VerifyRevokedCertificate (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler, SMimeCertificate certificate, bool validateCreate) + protected void VerifyRevokedCertificate (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler, SMimeCertificate certificate) { var body = new TextPart ("plain") { Text = "This is some cleartext that we'll end up signing..." }; - var signer = new CmsSigner (certificate.FileName, "no.secret"); - ctx.CheckCertificateRevocation = validateCreate; var multipart = MultipartSigned.Create (ctx, signer, body); - ctx.CheckCertificateRevocation = true; - - if (validateCreate) - AssertCrlsRequested (mockHttpMessageHandler); - else - AssertCrlsNotRequested (mockHttpMessageHandler); Assert.That (multipart.Count, Is.EqualTo (2), "The multipart/signed has an unexpected number of children."); @@ -2981,19 +2977,11 @@ protected void VerifyRevokedCertificate (BouncyCastleSecureMimeContext ctx, Mock } } - protected async Task VerifyRevokedCertificateAsync (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler, SMimeCertificate certificate, bool validateCreate) + protected async Task VerifyRevokedCertificateAsync (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler, SMimeCertificate certificate) { var body = new TextPart ("plain") { Text = "This is some cleartext that we'll end up signing..." }; - var signer = new CmsSigner (certificate.FileName, "no.secret"); - ctx.CheckCertificateRevocation = validateCreate; var multipart = await MultipartSigned.CreateAsync (ctx, signer, body); - ctx.CheckCertificateRevocation = true; - - if (validateCreate) - AssertCrlsRequested (mockHttpMessageHandler); - else - AssertCrlsNotRequested (mockHttpMessageHandler); Assert.That (multipart.Count, Is.EqualTo (2), "The multipart/signed has an unexpected number of children."); @@ -3056,7 +3044,7 @@ protected async Task VerifyRevokedCertificateAsync (BouncyCastleSecureMimeContex } } - protected void VerifyCrlsResolvedWithBuildCertificateChain (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler) + protected void VerifyCrlsResolved (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler) { var body = new TextPart ("plain") { Text = "This is some cleartext that we'll end up signing..." }; var certificate = SupportedCertificates.Single (c => c.EmailAddress == "nochain@mimekit.net"); @@ -3080,13 +3068,13 @@ protected void VerifyCrlsResolvedWithBuildCertificateChain (BouncyCastleSecureMi AssertValidSignatures (ctx, signatures); } - protected void VerifyCrlsResolved (BouncyCastleSecureMimeContext ctx, Mock mockHttpMessageHandler) + protected static void VerifyCrlMissing (BouncyCastleSecureMimeContext ctx, string errorContent) { var body = new TextPart ("plain") { Text = "This is some cleartext that we'll end up signing..." }; var certificate = SupportedCertificates.Single (c => c.EmailAddress == "nochain@mimekit.net"); var signer = new CmsSigner (certificate.FileName, "no.secret"); - var multipart = MultipartSigned.Create (ctx, signer, body); + using var multipart = MultipartSigned.Create (ctx, signer, body); Assert.That (multipart.Count, Is.EqualTo (2), "The multipart/signed has an unexpected number of children."); @@ -3099,73 +3087,11 @@ protected void VerifyCrlsResolved (BouncyCastleSecureMimeContext ctx, Mock c.EmailAddress == "nochain@mimekit.net"); - - var signer = new CmsSigner (certificate.FileName, "no.secret"); - using var multipart = MultipartSigned.Create (ctx, signer, body); - - Assert.That (multipart.Count, Is.EqualTo (2), "The multipart/signed has an unexpected number of children."); - - var protocol = multipart.ContentType.Parameters["protocol"]; - Assert.That (protocol, Is.EqualTo (ctx.SignatureProtocol), "The multipart/signed protocol does not match."); - - Assert.That (multipart[0], Is.InstanceOf (), "The first child is not a text part."); - Assert.That (multipart[1], Is.InstanceOf (), "The second child is not a detached signature."); - - var signatures = multipart.Verify (ctx); - Assert.That (signatures.Count, Is.EqualTo (1), "Verify returned an unexpected number of signatures."); - var ex = Assert.Throws (() => signatures.Single ().Verify ()); Assert.That (ex.Message, Does.StartWith ("Failed to verify digital signature chain: Certification path could not be validated.")); Assert.That (ex.InnerException?.Message, Does.StartWith ("Certification path could not be validated.")); Assert.That(ex.InnerException?.InnerException?.Message, Is.EquivalentTo ($"No CRLs found for issuer \"{errorContent}\"")); } - - - protected void VerifyMimeEncapsulatedSigningWithContext (BouncyCastleSecureMimeContext ctx, - Mock mockHttpMessageHandler) - { - var cleartext = new TextPart ("plain") { Text = "This is some text that we'll end up signing..." }; - var certificate = SupportedCertificates.Single (c => c.EmailAddress == "nochain@mimekit.net"); - - var self = new MailboxAddress ("MimeKit UnitTests", certificate.EmailAddress); - var signed = ApplicationPkcs7Mime.Sign (ctx, self, DigestAlgorithm.Sha1, cleartext); - - AssertCrlsRequested (mockHttpMessageHandler); - - MimeEntity extracted; - - Assert.That (signed.SecureMimeType, Is.EqualTo (SecureMimeType.SignedData), "S/MIME type did not match."); - - var signatures = signed.Verify (ctx, out extracted); - - Assert.That (extracted, Is.InstanceOf (), "Extracted part is not the expected type."); - Assert.That (((TextPart) extracted).Text, Is.EqualTo (cleartext.Text), "Extracted content is not the same as the original."); - - Assert.That (signatures.Count, Is.EqualTo (1), "Verify returned an unexpected number of signatures."); - AssertValidSignatures (ctx, signatures); - - using (var signedData = signed.Content.Open ()) { - using (var stream = ctx.Verify (signedData, out signatures)) - extracted = MimeEntity.Load (stream); - - Assert.That (extracted, Is.InstanceOf (), "Extracted part is not the expected type."); - Assert.That (((TextPart) extracted).Text, Is.EqualTo (cleartext.Text), "Extracted content is not the same as the original."); - - Assert.That (signatures.Count, Is.EqualTo (1), "Verify returned an unexpected number of signatures."); - AssertValidSignatures (ctx, signatures); - } - - AssertCrlsRequested (mockHttpMessageHandler); - } } [TestFixture] @@ -3205,56 +3131,40 @@ protected override SecureMimeContext CreateContext () [Test] public void TestVerifyRevokedCertificate () { - using (var ctx = new MyTemporarySecureMimeContext ()) { - ImportTestCertificates (ctx); - - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, false); - } - - using (var ctx = new MyTemporarySecureMimeContext ()) { - ImportTestCertificates (ctx); - - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, true); - } - - using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { - ImportTestCertificates (ctx); - - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, false); - } - - using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + using (var ctx = new MyTemporarySecureMimeContext () { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, true); + VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedCertificate); } } [Test] public async Task TestVerifyRevokedCertificateAsync () { - using (var ctx = new MyTemporarySecureMimeContext ()) { - ImportTestCertificates (ctx); - - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, false); - } - - using (var ctx = new MyTemporarySecureMimeContext ()) { + using (var ctx = new MyTemporarySecureMimeContext () { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, true); + + await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedCertificate); } + } - using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + [Test] + public void TestVerifyRevokedNoChainCertificate () + { + using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ())) { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, false); + + VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate); } + } - using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + [Test] + public async Task TestVerifyRevokedNoChainCertificateAsync () + { + using (var ctx = new MyTemporarySecureMimeContext (CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ())) { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, true); + + await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate); } } @@ -3278,7 +3188,7 @@ public void TestMissingRootCrl () var crlUrlIndexes = new[] { 1, 2 }; var errorContent = RootCertificate.SubjectDN.ToString (); - VerifyCrlMissingTest (responses, crlUrlIndexes, errorContent); + TestVerifyCrlMissing (responses, crlUrlIndexes, errorContent); } [Test] @@ -3291,7 +3201,7 @@ public void TestMissingPrimaryIntermediateCrl () var crlUrlIndexes = new[] { 0, 2 }; var errorContent = IntermediateCertificate1.SubjectDN.ToString (); - VerifyCrlMissingTest (responses, crlUrlIndexes, errorContent); + TestVerifyCrlMissing (responses, crlUrlIndexes, errorContent); } [Test] @@ -3304,10 +3214,10 @@ public void TestMissingSecondaryIntermediateCrl () var crlUrlIndexes = new[] { 0, 1 }; var errorContent = IntermediateCertificate2.SubjectDN.ToString (); - VerifyCrlMissingTest (responses, crlUrlIndexes, errorContent); + TestVerifyCrlMissing (responses, crlUrlIndexes, errorContent); } - private void VerifyCrlMissingTest (HttpResponseMessage[] responses, int[] crlUrlIndexes, string errorContent) + private void TestVerifyCrlMissing (HttpResponseMessage[] responses, int[] crlUrlIndexes, string errorContent) { var mockHttpMessageHandler = new Mock (MockBehavior.Strict); @@ -3330,16 +3240,6 @@ private void VerifyCrlMissingTest (HttpResponseMessage[] responses, int[] crlUrl VerifyCrlMissing (ctx, errorContent); } } - - [Test] - public virtual void TestVerifyCrlsResolvedWithSecureMimeEncapsulatedSigningWithContext () - { - using (var ctx = new MyTemporarySecureMimeContext () { CheckCertificateRevocation = true }) { - ImportTestCertificates (ctx); - - VerifyMimeEncapsulatedSigningWithContext (ctx, ctx.MockHttpMessageHandler); - } - } } [TestFixture] @@ -3387,70 +3287,46 @@ static DefaultSecureMimeTests () [Test] public void TestVerifyRevokedCertificate () { - using (var ctx = new MySecureMimeContext ("revoked.db", "no.secret")) { + using (var ctx = new MySecureMimeContext ("revoked.db", "no.secret") { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, false); + VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedCertificate); } File.Delete ("revoked.db"); - - using (var ctx = new MySecureMimeContext ("revoked.db", "no.secret")) { - ImportTestCertificates (ctx); - - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, true); - } - - File.Delete ("revoked.db"); - - using (var ctx = new MySecureMimeContext ("revoked.db", "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { - ImportTestCertificates (ctx); - - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, false); - } - - File.Delete ("revoked.db"); - - using (var ctx = new MySecureMimeContext ("revoked.db", "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { - ImportTestCertificates (ctx); - - VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, true); - } - - File.Delete ("revoked.db"); } [Test] public async Task TestVerifyRevokedCertificateAsync () { - using (var ctx = new MySecureMimeContext ("revoked-async.db", "no.secret")) { - ImportTestCertificates (ctx); - - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, false); - } - - File.Delete ("revoked-async.db"); - - using (var ctx = new MySecureMimeContext ("revoked-async.db", "no.secret")) { + using (var ctx = new MySecureMimeContext ("revoked-async.db", "no.secret") { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedCertificate, true); + await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedCertificate); } File.Delete ("revoked-async.db"); + } - using (var ctx = new MySecureMimeContext ("revoked-async.db", "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + [Test] + public void TestVerifyRevokedNoChainCertificate () + { + using (var ctx = new MySecureMimeContext ("revoked.db", "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ())) { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, false); + VerifyRevokedCertificate (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate); } - File.Delete ("revoked-async.db"); + File.Delete ("revoked.db"); + } - using (var ctx = new MySecureMimeContext ("revoked-async.db", "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ()))) { + [Test] + public async Task TestVerifyRevokedNoChainCertificateAsync () + { + using (var ctx = new MySecureMimeContext ("revoked-async.db", "no.secret", CreateMockHttpMessageHandler (RevokedNoChainCertificateResponses ())) { CheckCertificateRevocation = true }) { ImportTestCertificates (ctx); - await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate, true); + await VerifyRevokedCertificateAsync (ctx, ctx.MockHttpMessageHandler, RevokedNoChainCertificate); } File.Delete ("revoked-async.db");