Remove System.Linq dependency from System.Security.Cryptography.X509Certificates
authorStephen Toub <stoub@microsoft.com>
Tue, 8 Oct 2019 16:57:21 +0000 (12:57 -0400)
committerJeremy Barton <jbarton@microsoft.com>
Tue, 8 Oct 2019 16:57:21 +0000 (09:57 -0700)
System.Linq was only being used in two places:
- In Pkcs10CertificateRequestInfo, it was using Collection<>.Select(...).ToArray().  We can replace it with a simple for loop, which is both faster (e.g. fewer delegate invocations) and fewer generic instantiations (no one else is going to have an instantiation with AttributeAsn).
- In LoadMachineStores on Linux, it was using Prepend.  We can avoid the need for Prepend (and the associated allocations) entirely by slightly reorganizing the method and using a local function.

Commit migrated from https://github.com/dotnet/corefx/commit/5ad15fcb189a9c232051d03c1e889f985f62232b

src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CachedSystemStoreProvider.cs
src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs10CertificationRequestInfo.cs

index edaf573..d25711d 100644 (file)
@@ -129,35 +129,33 @@ namespace Internal.Cryptography.Pal
                 Monitor.IsEntered(s_recheckStopwatch),
                 "LoadMachineStores assumes a lock(s_recheckStopwatch)");
 
-            IEnumerable<FileInfo> trustedCertFiles;
+            SafeX509StackHandle rootStore = Interop.Crypto.NewX509Stack();
+            Interop.Crypto.CheckValidOpenSslHandle(rootStore);
+            SafeX509StackHandle intermedStore = Interop.Crypto.NewX509Stack();
+            Interop.Crypto.CheckValidOpenSslHandle(intermedStore);
+
             DateTime newFileTime = default;
             DateTime newDirTime = default;
 
-            if (rootStorePath != null && rootStorePath.Exists)
-            {
-                trustedCertFiles = rootStorePath.EnumerateFiles();
-                newDirTime = rootStorePath.LastWriteTimeUtc;
-            }
-            else
-            {
-                trustedCertFiles = Array.Empty<FileInfo>();
-            }
+            var uniqueRootCerts = new HashSet<X509Certificate2>();
+            var uniqueIntermediateCerts = new HashSet<X509Certificate2>();
 
             if (rootStoreFile != null && rootStoreFile.Exists)
             {
-                trustedCertFiles = trustedCertFiles.Prepend(rootStoreFile);
                 newFileTime = rootStoreFile.LastWriteTimeUtc;
+                ProcessFile(rootStoreFile);
             }
 
-            SafeX509StackHandle rootStore = Interop.Crypto.NewX509Stack();
-            Interop.Crypto.CheckValidOpenSslHandle(rootStore);
-            SafeX509StackHandle intermedStore = Interop.Crypto.NewX509Stack();
-            Interop.Crypto.CheckValidOpenSslHandle(intermedStore);
-
-            HashSet<X509Certificate2> uniqueRootCerts = new HashSet<X509Certificate2>();
-            HashSet<X509Certificate2> uniqueIntermediateCerts = new HashSet<X509Certificate2>();
+            if (rootStorePath != null && rootStorePath.Exists)
+            {
+                newDirTime = rootStorePath.LastWriteTimeUtc;
+                foreach (FileInfo file in rootStorePath.EnumerateFiles())
+                {
+                    ProcessFile(file);
+                }
+            }
 
-            foreach (FileInfo file in trustedCertFiles)
+            void ProcessFile(FileInfo file)
             {
                 using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file.FullName, "rb"))
                 {
@@ -165,17 +163,16 @@ namespace Internal.Cryptography.Pal
                     if (fileBio.IsInvalid)
                     {
                         Interop.Crypto.ErrClearError();
-                        continue;
+                        return;
                     }
 
-                    ICertificatePal pal;
-
                     // Some distros ship with two variants of the same certificate.
                     // One is the regular format ('BEGIN CERTIFICATE') and the other
                     // contains additional AUX-data ('BEGIN TRUSTED CERTIFICATE').
                     // The additional data contains the appropriate usage (e.g. emailProtection, serverAuth, ...).
                     // Because corefx doesn't validate for a specific usage, derived certificates are rejected.
                     // For now, we skip the certificates with AUX data and use the regular certificates.
+                    ICertificatePal pal;
                     while (OpenSslX509CertificateReader.TryReadX509PemNoAux(fileBio, out pal) ||
                         OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal))
                     {
index 56efaeb..68b4b68 100644 (file)
     <Reference Include="System.Diagnostics.Debug" />
     <Reference Include="System.Diagnostics.Tools" />
     <Reference Include="System.IO.FileSystem" />
-    <Reference Include="System.Linq" />
     <Reference Include="System.Memory" />
     <Reference Include="System.Net.Primitives" />
     <Reference Include="System.Resources.ResourceManager" />
index 47c6139..522a1fa 100644 (file)
@@ -5,7 +5,6 @@
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
-using System.Linq;
 using System.Security.Cryptography.Asn1;
 using System.Security.Cryptography.X509Certificates.Asn1;
 using Internal.Cryptography;
@@ -58,12 +57,18 @@ namespace System.Security.Cryptography.X509Certificates
             spki.Algorithm = new AlgorithmIdentifierAsn { Algorithm = PublicKey.Oid, Parameters = PublicKey.EncodedParameters.RawData };
             spki.SubjectPublicKey = PublicKey.EncodedKeyValue.RawData;
 
+            var attributes = new AttributeAsn[Attributes.Count];
+            for (int i = 0; i < attributes.Length; i++)
+            {
+                attributes[i] = new AttributeAsn(Attributes[i]);
+            }
+
             CertificationRequestInfoAsn requestInfo = new CertificationRequestInfoAsn
             {
                 Version = 0,
                 Subject = this.Subject.RawData,
                 SubjectPublicKeyInfo = spki,
-                Attributes = Attributes.Select(a => new AttributeAsn(a)).ToArray(),
+                Attributes = attributes
             };
 
             using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))