Skip to content

Commit f05cefb

Browse files
committed
Make ValidateCertificateChain/Async() private for now
These might be valuable to make public at some point, but when we do, we might want to provide more info as to why they did not validate. Unfortunately, BouncyCastle does not make that easy to programatically determine via their APIs, so maybe we'd have to live with exceptions. Either way, true/false might not be ideal for a public API. Unit Test changes are all code formatting/style related changes.
1 parent ea87b4d commit f05cefb

5 files changed

+44
-65
lines changed

MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs

+18-23
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
using System;
2828
using System.IO;
2929
using System.Net;
30+
using System.Linq;
3031
using System.Net.Http;
3132
using System.Threading;
3233
using System.Threading.Tasks;
@@ -56,8 +57,6 @@
5657
using IssuerAndSerialNumber = Org.BouncyCastle.Asn1.Cms.IssuerAndSerialNumber;
5758

5859
using MimeKit.IO;
59-
using System.Linq;
60-
using Org.BouncyCastle.Tls;
6160

6261
namespace MimeKit.Cryptography {
6362
/// <summary>
@@ -68,6 +67,7 @@ namespace MimeKit.Cryptography {
6867
/// </remarks>
6968
public abstract class BouncyCastleSecureMimeContext : SecureMimeContext
7069
{
70+
static readonly X509CertStoreSelector MatchAllCertificates = new X509CertStoreSelector ();
7171
static readonly string RsassaPssOid = PkcsObjectIdentifiers.IdRsassaPss.Id;
7272
static readonly HttpClient SharedHttpClient = new HttpClient ();
7373

@@ -171,9 +171,9 @@ protected virtual HttpClient HttpClient {
171171
/// generally issued by a certificate authority (CA).</para>
172172
/// <para>This method is used to build a certificate chain while verifying
173173
/// signed content.</para>
174-
/// <para>It is critical to always load the designated trust anchors
175-
/// and not the anchor in the end certificate when building a certificate chain
176-
/// to validated trust.</para>
174+
/// <para>It is critical to always load the designated trust anchors,
175+
/// and not the anchor in the end certificate, when building a certificate chain
176+
/// when validating trust.</para>
177177
/// </remarks>
178178
/// <returns>The trusted anchors.</returns>
179179
protected abstract ISet<TrustAnchor> GetTrustedAnchors ();
@@ -348,7 +348,7 @@ Stream Sign (CmsSigner signer, Stream content, bool encapsulate, CancellationTok
348348
async Task<Stream> SignAsync (CmsSigner signer, Stream content, bool encapsulate, CancellationToken cancellationToken)
349349
{
350350
if (CheckCertificateRevocation)
351-
await ValidateCertificateChainAsync (signer.CertificateChain, DateTime.UtcNow, cancellationToken);
351+
await ValidateCertificateChainAsync (signer.CertificateChain, DateTime.UtcNow, cancellationToken).ConfigureAwait (false);
352352

353353
var signedData = CreateSignedDataGenerator (signer);
354354
var memory = new MemoryBlockStream ();
@@ -713,9 +713,8 @@ protected IList<X509Certificate> BuildCertificateChain (X509Certificate certific
713713
var issuerStore = GetTrustedAnchors ();
714714
var anchorStore = new X509CertificateStore ();
715715

716-
foreach (var anchor in issuerStore) {
716+
foreach (var anchor in issuerStore)
717717
anchorStore.Add (anchor.TrustedCert);
718-
}
719718

720719
var parameters = new PkixBuilderParameters (issuerStore, selector) {
721720
ValidityModel = PkixParameters.PkixValidityModel,
@@ -726,7 +725,7 @@ protected IList<X509Certificate> BuildCertificateChain (X509Certificate certific
726725

727726
var intermediateStore = GetIntermediateCertificates ();
728727

729-
foreach (var intermediate in intermediateStore.EnumerateMatches (new X509CertStoreSelector ()))
728+
foreach (var intermediate in intermediateStore.EnumerateMatches (MatchAllCertificates))
730729
anchorStore.Add (intermediate);
731730

732731
parameters.AddStoreCert (anchorStore);
@@ -761,7 +760,7 @@ protected IList<X509Certificate> BuildCertificateChain (X509Certificate certific
761760
/// <exception cref="ArgumentException">
762761
/// <paramref name="chain"/> is empty or contains a <see langword="null"/> certificate.
763762
/// </exception>
764-
public bool ValidateCertificateChain (X509CertificateChain chain, DateTime dateTime, CancellationToken cancellationToken = default)
763+
bool ValidateCertificateChain (X509CertificateChain chain, DateTime dateTime, CancellationToken cancellationToken = default)
765764
{
766765
if (chain == null)
767766
throw new ArgumentNullException (nameof (chain));
@@ -780,9 +779,8 @@ public bool ValidateCertificateChain (X509CertificateChain chain, DateTime dateT
780779
var issuerStore = GetTrustedAnchors ();
781780
var anchorStore = new X509CertificateStore ();
782781

783-
foreach (var anchor in issuerStore) {
782+
foreach (var anchor in issuerStore)
784783
anchorStore.Add (anchor.TrustedCert);
785-
}
786784

787785
var parameters = new PkixBuilderParameters (issuerStore, selector) {
788786
ValidityModel = PkixParameters.PkixValidityModel,
@@ -798,7 +796,7 @@ public bool ValidateCertificateChain (X509CertificateChain chain, DateTime dateT
798796

799797
var intermediateStore = GetIntermediateCertificates ();
800798

801-
foreach (var intermediate in intermediateStore.EnumerateMatches (new X509CertStoreSelector ())) {
799+
foreach (var intermediate in intermediateStore.EnumerateMatches (MatchAllCertificates)) {
802800
anchorStore.Add (intermediate);
803801
if (CheckCertificateRevocation)
804802
DownloadCrls (intermediate, cancellationToken);
@@ -837,7 +835,7 @@ public bool ValidateCertificateChain (X509CertificateChain chain, DateTime dateT
837835
/// <exception cref="ArgumentException">
838836
/// <paramref name="chain"/> is empty or contains a <see langword="null"/> certificate.
839837
/// </exception>
840-
public async Task<bool> ValidateCertificateChainAsync (X509CertificateChain chain, DateTime dateTime, CancellationToken cancellationToken = default)
838+
async Task<bool> ValidateCertificateChainAsync (X509CertificateChain chain, DateTime dateTime, CancellationToken cancellationToken = default)
841839
{
842840
if (chain == null)
843841
throw new ArgumentNullException (nameof (chain));
@@ -856,9 +854,8 @@ public async Task<bool> ValidateCertificateChainAsync (X509CertificateChain chai
856854
var issuerStore = GetTrustedAnchors ();
857855
var anchorStore = new X509CertificateStore ();
858856

859-
foreach (var anchor in issuerStore) {
857+
foreach (var anchor in issuerStore)
860858
anchorStore.Add (anchor.TrustedCert);
861-
}
862859

863860
var parameters = new PkixBuilderParameters (issuerStore, selector) {
864861
ValidityModel = PkixParameters.PkixValidityModel,
@@ -874,7 +871,7 @@ public async Task<bool> ValidateCertificateChainAsync (X509CertificateChain chai
874871

875872
var intermediateStore = GetIntermediateCertificates ();
876873

877-
foreach (var intermediate in intermediateStore.EnumerateMatches (new X509CertStoreSelector ())) {
874+
foreach (var intermediate in intermediateStore.EnumerateMatches (MatchAllCertificates)) {
878875
anchorStore.Add (intermediate);
879876
if (CheckCertificateRevocation)
880877
await DownloadCrlsAsync (intermediate, cancellationToken).ConfigureAwait (false);
@@ -1169,7 +1166,7 @@ static IEnumerable<string> EnumerateCrlDistributionPointUrls (X509Certificate ce
11691166
}
11701167
}
11711168

1172-
void DownloadCrls (X509Certificate certificate, CancellationToken cancellationToken = default)
1169+
void DownloadCrls (X509Certificate certificate, CancellationToken cancellationToken)
11731170
{
11741171
var nextUpdate = GetNextCertificateRevocationListUpdate (certificate.IssuerDN);
11751172
var now = DateTime.UtcNow;
@@ -1305,9 +1302,8 @@ DigitalSignatureCollection GetDigitalSignatures (CmsSignedDataParser parser, Can
13051302
foreach (var anchor in anchors)
13061303
DownloadCrls (anchor.TrustedCert, cancellationToken);
13071304

1308-
foreach (X509Certificate intermediate in intermediates.EnumerateMatches(new X509CertStoreSelector())) {
1305+
foreach (var intermediate in intermediates.EnumerateMatches (MatchAllCertificates))
13091306
DownloadCrls (intermediate, cancellationToken);
1310-
}
13111307
}
13121308

13131309
try {
@@ -1364,9 +1360,8 @@ async Task<DigitalSignatureCollection> GetDigitalSignaturesAsync (CmsSignedDataP
13641360
foreach (var anchor in anchors)
13651361
await DownloadCrlsAsync (anchor.TrustedCert, cancellationToken).ConfigureAwait (false);
13661362

1367-
foreach (X509Certificate intermediate in intermediates.EnumerateMatches (new X509CertStoreSelector ())) {
1368-
await DownloadCrlsAsync (intermediate, cancellationToken);
1369-
}
1363+
foreach (var intermediate in intermediates.EnumerateMatches (MatchAllCertificates))
1364+
await DownloadCrlsAsync (intermediate, cancellationToken).ConfigureAwait (false);
13701365
}
13711366

13721367
try {

MimeKit/Cryptography/DefaultSecureMimeContext.cs

+8-4
Original file line numberDiff line numberDiff line change
@@ -409,11 +409,13 @@ protected override AsymmetricKeyParameter GetPrivateKey (ISelector<X509Certifica
409409
protected override ISet<TrustAnchor> GetTrustedAnchors ()
410410
{
411411
var anchors = new HashSet<TrustAnchor> ();
412-
var selector = new X509CertStoreSelector ();
413412
var keyUsage = new bool[9];
414413

415414
keyUsage[(int) X509KeyUsageBits.KeyCertSign] = true;
416-
selector.KeyUsage = keyUsage;
415+
416+
var selector = new X509CertStoreSelector {
417+
KeyUsage = keyUsage
418+
};
417419

418420
foreach (var record in dbase.Find (selector, true, X509CertificateRecordFields.Certificate))
419421
anchors.Add (new TrustAnchor (record.Certificate, null));
@@ -433,11 +435,13 @@ protected override ISet<TrustAnchor> GetTrustedAnchors ()
433435
protected override IStore<X509Certificate> GetIntermediateCertificates ()
434436
{
435437
var intermediates = new X509CertificateStore ();
436-
var selector = new X509CertStoreSelector ();
437438
var keyUsage = new bool[9];
438439

439440
keyUsage[(int) X509KeyUsageBits.KeyCertSign] = true;
440-
selector.KeyUsage = keyUsage;
441+
442+
var selector = new X509CertStoreSelector {
443+
KeyUsage = keyUsage
444+
};
441445

442446
foreach (var record in dbase.Find (selector, false, X509CertificateRecordFields.Certificate)) {
443447
if (!record.Certificate.IsSelfSigned ())

UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs

-13
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,6 @@
3535
using MimeKit.Cryptography;
3636

3737
using BCX509Certificate = Org.BouncyCastle.X509.X509Certificate;
38-
using Org.BouncyCastle.Cms;
39-
using Org.BouncyCastle.Crypto;
40-
using Org.BouncyCastle.OpenSsl;
41-
42-
using Org.BouncyCastle.Crypto;
43-
using Org.BouncyCastle.OpenSsl;
44-
using Org.BouncyCastle.Security;
45-
using Org.BouncyCastle.X509;
46-
using Org.BouncyCastle.Pkcs;
47-
using Org.BouncyCastle.Cms;
48-
using Org.BouncyCastle.Crypto.Encodings;
49-
using Org.BouncyCastle.Crypto.Engines;
50-
using Org.BouncyCastle.Crypto.Parameters;
5138

5239
namespace UnitTests.Cryptography {
5340
[TestFixture]

UnitTests/Cryptography/SecureMimeTests.cs

+15-22
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ public abstract class SecureMimeTestsBase
8080
public const string ThunderbirdName = "[email protected]";
8181

8282
public static readonly string[] RelativeConfigFilePaths = {
83-
"certificate-authority.cfg", "intermediate1.cfg", "intermediate2.cfg", "dnsnames/smime.cfg", "dsa/smime.cfg", "ec/smime.cfg", "nochain/smime.cfg", "revoked/smime.cfg", "revokednochain/smime.cfg", "rsa/smime.cfg"
83+
"certificate-authority.cfg", "intermediate1.cfg", "intermediate2.cfg", "dnsnames/smime.cfg", "dsa/smime.cfg",
84+
"ec/smime.cfg", "nochain/smime.cfg", "revoked/smime.cfg", "revokednochain/smime.cfg", "rsa/smime.cfg"
8485
};
8586

8687
public static readonly string[] StartComCertificates = {
@@ -2993,7 +2994,6 @@ protected async Task VerifyRevokedCertificateAsync (BouncyCastleSecureMimeContex
29932994
AssertCrlsRequested (mockHttpMessageHandler);
29942995
else
29952996
AssertCrlsNotRequested (mockHttpMessageHandler);
2996-
29972997

29982998
Assert.That (multipart.Count, Is.EqualTo (2), "The multipart/signed has an unexpected number of children.");
29992999

@@ -3056,11 +3056,10 @@ protected async Task VerifyRevokedCertificateAsync (BouncyCastleSecureMimeContex
30563056
}
30573057
}
30583058

3059-
protected void VerifyCrlsResolvedWithBuildCertificateChain (BouncyCastleSecureMimeContext ctx,
3060-
Mock<HttpMessageHandler> mockHttpMessageHandler)
3059+
protected void VerifyCrlsResolvedWithBuildCertificateChain (BouncyCastleSecureMimeContext ctx, Mock<HttpMessageHandler> mockHttpMessageHandler)
30613060
{
30623061
var body = new TextPart ("plain") { Text = "This is some cleartext that we'll end up signing..." };
3063-
var certificate = SupportedCertificates.Single(c => c.EmailAddress == "[email protected]");
3062+
var certificate = SupportedCertificates.Single (c => c.EmailAddress == "[email protected]");
30643063

30653064
var signer = new CmsSigner (certificate.FileName, "no.secret");
30663065
var multipart = MultipartSigned.Create (ctx, signer, body);
@@ -3081,8 +3080,7 @@ protected void VerifyCrlsResolvedWithBuildCertificateChain (BouncyCastleSecureMi
30813080
AssertValidSignatures (ctx, signatures);
30823081
}
30833082

3084-
protected void VerifyCrlsResolved (BouncyCastleSecureMimeContext ctx,
3085-
Mock<HttpMessageHandler> mockHttpMessageHandler)
3083+
protected void VerifyCrlsResolved (BouncyCastleSecureMimeContext ctx, Mock<HttpMessageHandler> mockHttpMessageHandler)
30863084
{
30873085
var body = new TextPart ("plain") { Text = "This is some cleartext that we'll end up signing..." };
30883086
var certificate = SupportedCertificates.Single (c => c.EmailAddress == "[email protected]");
@@ -3136,8 +3134,6 @@ protected void VerifyMimeEncapsulatedSigningWithContext (BouncyCastleSecureMimeC
31363134
Mock<HttpMessageHandler> mockHttpMessageHandler)
31373135
{
31383136
var cleartext = new TextPart ("plain") { Text = "This is some text that we'll end up signing..." };
3139-
3140-
31413137
var certificate = SupportedCertificates.Single (c => c.EmailAddress == "[email protected]");
31423138

31433139
var self = new MailboxAddress ("MimeKit UnitTests", certificate.EmailAddress);
@@ -3275,10 +3271,9 @@ public void TestVerifyCrlsResolved ()
32753271
[Test]
32763272
public void TestMissingRootCrl ()
32773273
{
3278-
var responses = new HttpResponseMessage[]
3279-
{
3280-
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(CurrentCrls[1].GetEncoded()) },
3281-
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(CurrentCrls[2].GetEncoded()) }
3274+
var responses = new HttpResponseMessage[] {
3275+
new HttpResponseMessage (HttpStatusCode.OK) { Content = new ByteArrayContent (CurrentCrls[1].GetEncoded ()) },
3276+
new HttpResponseMessage (HttpStatusCode.OK) { Content = new ByteArrayContent (CurrentCrls[2].GetEncoded ()) }
32823277
};
32833278
var crlUrlIndexes = new[] { 1, 2 };
32843279
var errorContent = RootCertificate.SubjectDN.ToString ();
@@ -3289,10 +3284,9 @@ public void TestMissingRootCrl ()
32893284
[Test]
32903285
public void TestMissingPrimaryIntermediateCrl ()
32913286
{
3292-
var responses = new HttpResponseMessage[]
3293-
{
3294-
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(CurrentCrls[0].GetEncoded()) },
3295-
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(CurrentCrls[2].GetEncoded()) }
3287+
var responses = new HttpResponseMessage[] {
3288+
new HttpResponseMessage (HttpStatusCode.OK) { Content = new ByteArrayContent (CurrentCrls[0].GetEncoded ()) },
3289+
new HttpResponseMessage (HttpStatusCode.OK) { Content = new ByteArrayContent (CurrentCrls[2].GetEncoded ()) }
32963290
};
32973291
var crlUrlIndexes = new[] { 0, 2 };
32983292
var errorContent = IntermediateCertificate1.SubjectDN.ToString ();
@@ -3303,10 +3297,9 @@ public void TestMissingPrimaryIntermediateCrl ()
33033297
[Test]
33043298
public void TestMissingSecondaryIntermediateCrl ()
33053299
{
3306-
var responses = new HttpResponseMessage[]
3307-
{
3308-
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(CurrentCrls[0].GetEncoded()) },
3309-
new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(CurrentCrls[1].GetEncoded()) }
3300+
var responses = new HttpResponseMessage[] {
3301+
new HttpResponseMessage (HttpStatusCode.OK) { Content = new ByteArrayContent (CurrentCrls[0].GetEncoded ()) },
3302+
new HttpResponseMessage (HttpStatusCode.OK) { Content = new ByteArrayContent (CurrentCrls[1].GetEncoded ()) }
33103303
};
33113304
var crlUrlIndexes = new[] { 0, 1 };
33123305
var errorContent = IntermediateCertificate2.SubjectDN.ToString ();
@@ -3365,7 +3358,7 @@ public MySecureMimeContext (string database, string password, Mock<HttpMessageHa
33653358
{
33663359
CheckCertificateRevocation = false;
33673360

3368-
MockHttpMessageHandler = mockHttpMessageHandler?? CreateMockHttpMessageHandler (RevokedCertificateResponses ());
3361+
MockHttpMessageHandler = mockHttpMessageHandler ?? CreateMockHttpMessageHandler (RevokedCertificateResponses ());
33693362
client = new HttpClient (MockHttpMessageHandler.Object);
33703363
}
33713364

UnitTests/Cryptography/X509CertificateGenerator.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ public GeneratorOptions ()
309309
{
310310
IssuerPassword = string.Empty;
311311
Password = string.Empty;
312+
IncludeChain = true;
312313
}
313314

314315
public string BasicConstraints { get; set; }
@@ -327,7 +328,7 @@ public GeneratorOptions ()
327328

328329
public string SignatureAlgorithm { get; set; }
329330

330-
public bool IncludeChain { get; set; } = true;
331+
public bool IncludeChain { get; set; }
331332
}
332333

333334
public static X509Certificate[] Generate (GeneratorOptions options, PrivateKeyOptions privateKeyOptions, CertificateOptions certificateOptions, out AsymmetricKeyParameter privateKey)
@@ -529,7 +530,7 @@ public static X509Certificate[] Generate (GeneratorOptions options, PrivateKeyOp
529530

530531

531532
for (int i = 0; i < chain.Length; i++) {
532-
if(options.IncludeChain)
533+
if (options.IncludeChain)
533534
chainEntries[i + 1] = new X509CertificateEntry (chain[i]);
534535
certificates[i + 1] = chain[i];
535536
}
@@ -675,7 +676,6 @@ public static X509Certificate[] Generate (string cfg, out AsymmetricKeyParameter
675676
case "includechain":
676677
options.IncludeChain = bool.Parse (value);
677678
break;
678-
679679
default:
680680
throw new FormatException ($"Unknown [Generator] property: {property}");
681681
}

0 commit comments

Comments
 (0)