< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 189
Coverable lines: 189
Total lines: 340
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 72
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.ObjectModel;
 5using System.ComponentModel;
 6using System.Security.Cryptography.X509Certificates;
 7
 8namespace System.Net.Security
 9{
 10    public partial class SslStreamCertificateContext
 11    {
 12        internal readonly SslCertificateTrust? Trust;
 13
 14        /// <summary>
 15        /// Gets the target (leaf) certificate of the built chain.
 16        /// </summary>
 017        public X509Certificate2 TargetCertificate { get; }
 18
 19        /// <summary>
 20        /// Gets the intermediate certificates for the built chain.
 21        /// </summary>
 022        public ReadOnlyCollection<X509Certificate2> IntermediateCertificates { get; }
 23
 24        [EditorBrowsable(EditorBrowsableState.Never)]
 25        public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additional
 026        {
 027            return Create(target, additionalCertificates, offline, null);
 028        }
 29
 30        public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additional
 031        {
 032            return Create(target, additionalCertificates, offline, trust, noOcspFetch: false);
 033        }
 34
 35        internal static SslStreamCertificateContext Create(
 36            X509Certificate2 target,
 37            X509Certificate2Collection? additionalCertificates,
 38            bool offline,
 39            SslCertificateTrust? trust,
 40            bool noOcspFetch)
 041        {
 042            if (!target.HasPrivateKey)
 043            {
 044                throw new NotSupportedException(SR.net_ssl_io_no_server_cert);
 45            }
 46
 047            X509Certificate2[] intermediates = Array.Empty<X509Certificate2>();
 048            X509Certificate2? root = null;
 49
 050            using (X509Chain chain = new X509Chain())
 051            {
 052                if (additionalCertificates != null)
 053                {
 054                    chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates);
 055                }
 56
 057                if (trust != null)
 058                {
 059                    if (trust._store != null)
 060                    {
 061                        chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates);
 062                    }
 63
 064                    if (trust._trustList != null)
 065                    {
 066                        chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList);
 067                    }
 68
 069                    if (chain.ChainPolicy.CustomTrustStore.Count > 0)
 070                    {
 071                        chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
 072                    }
 073                }
 74
 075                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
 076                chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
 077                chain.ChainPolicy.DisableCertificateDownloads = offline;
 078                bool chainStatus = chain.Build(target);
 79
 080                if (!chainStatus && NetEventSource.Log.IsEnabled())
 081                {
 082                    NetEventSource.Error(null, $"Failed to build chain for {target.Subject}");
 083                }
 84
 085                if (!chainStatus && ChainBuildNeedsTrustedRoot && additionalCertificates?.Count > 0)
 086                {
 87                    // Some platforms like Android may not be able to build the chain unless the chain root is trusted.
 88                    // We can try to rebuild the chain with making all extra certificates trused.
 89                    // We do not try to evaluate trust here, we jsut need to construct the chain so it should not matter
 090                    chain.ChainPolicy.CustomTrustStore.AddRange(additionalCertificates);
 091                    chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
 092                    chainStatus = chain.Build(target);
 093                    if (!chainStatus && NetEventSource.Log.IsEnabled())
 094                    {
 095                        NetEventSource.Error(null, $"Failed to build chain for {target.Subject} while trusting additiona
 096                    }
 097                }
 98
 099                int count = chain.ChainElements.Count - 1;
 100
 101                // Some platforms (e.g. Android) can't ignore all verification and will return zero
 102                // certificates on failure to build a chain. Treat this as not finding any intermediates.
 0103                if (count >= 0)
 0104                {
 105#pragma warning disable 0162 // Disable unreachable code warning. TrimRootCertificate is const bool = false on some plat
 0106                    if (TrimRootCertificate)
 107                    {
 108                        count--;
 109                        root = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
 110
 111                        foreach (X509ChainStatus status in chain.ChainStatus)
 112                        {
 113                            if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain))
 114                            {
 115                                // The last cert isn't a root cert
 116                                count++;
 117                                root = null;
 118                                break;
 119                            }
 120                        }
 121                    }
 122#pragma warning restore 0162
 123
 124                    // Count can be zero for a self-signed certificate, or a cert issued directly from a root.
 0125                    if (count > 0 && chain.ChainElements.Count > 1)
 0126                    {
 0127                        intermediates = new X509Certificate2[count];
 0128                        for (int i = 0; i < count; i++)
 0129                        {
 0130                            intermediates[i] = chain.ChainElements[i + 1].Certificate;
 0131                        }
 0132                    }
 133
 134                    // Dispose the copy of the target cert.
 0135                    chain.ChainElements[0].Certificate.Dispose();
 136
 137                    // Dispose of the certificates that we do not need. If we are holding on to the root,
 138                    // don't dispose of it.
 0139                    int stopDisposingChainPosition = root is null ?
 0140                        chain.ChainElements.Count :
 0141                        chain.ChainElements.Count - 1;
 142
 0143                    for (int i = count + 1; i < stopDisposingChainPosition; i++)
 0144                    {
 0145                        chain.ChainElements[i].Certificate.Dispose();
 0146                    }
 0147                }
 0148            }
 149
 0150            SslStreamCertificateContext ctx = new SslStreamCertificateContext(target, new ReadOnlyCollection<X509Certifi
 151
 152            // On Linux, AddRootCertificate will start a background download of an OCSP response,
 153            // unless this context was built "offline", or this came from the internal Create(X509Certificate2)
 154            ctx.SetNoOcspFetch(offline || noOcspFetch);
 155
 0156            bool transferredOwnership = false;
 157            ctx.AddRootCertificate(root, ref transferredOwnership);
 158
 0159            if (!transferredOwnership)
 0160            {
 0161                root?.Dispose();
 0162            }
 163
 0164            return ctx;
 0165        }
 166
 167        partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool transferredOwnership);
 168        partial void SetNoOcspFetch(bool noOcspFetch);
 169
 170        internal SslStreamCertificateContext Duplicate()
 0171        {
 172            // Create will internally clone any certificates that it will
 173            // retain, so we don't have to duplicate any of the instances here
 0174            X509Certificate2Collection intermediates = new X509Certificate2Collection();
 0175            foreach (X509Certificate2 cert in IntermediateCertificates)
 0176            {
 0177                intermediates.Add(cert);
 0178            }
 179
 0180            return Create(new X509Certificate2(TargetCertificate), intermediates, trust: Trust);
 0181        }
 182
 183        internal void ReleaseResources()
 0184        {
 185            // TargetCertificate is owned by the user, but we have created the cert context
 186            // which looked up intermediate certificates and only we have reference to them.
 0187            foreach (X509Certificate2 cert in IntermediateCertificates)
 0188            {
 0189                cert.Dispose();
 0190            }
 191
 192            ReleasePlatformSpecificResources();
 0193        }
 194
 195        partial void ReleasePlatformSpecificResources();
 196    }
 197}
 198

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Windows.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.ObjectModel;
 5using System.Security.Cryptography;
 6using System.Security.Cryptography.X509Certificates;
 7
 8namespace System.Net.Security
 9{
 10    public partial class SslStreamCertificateContext
 11    {
 12        // No leaf, include root.
 13        private const bool TrimRootCertificate = false;
 14        private const bool ChainBuildNeedsTrustedRoot = false;
 15
 16        internal static SslStreamCertificateContext Create(X509Certificate2 target)
 17        {
 18            // On Windows we do not need to build chain unless we are asked for it.
 19            return new SslStreamCertificateContext(target, new ReadOnlyCollection<X509Certificate2>(Array.Empty<X509Cert
 20        }
 21
 022        private SslStreamCertificateContext(X509Certificate2 target, ReadOnlyCollection<X509Certificate2> intermediates,
 023        {
 024            if (intermediates.Count > 0)
 025            {
 026                using (X509Chain chain = new X509Chain())
 027                {
 028                    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
 029                    chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
 030                    chain.ChainPolicy.DisableCertificateDownloads = true;
 031                    bool osCanBuildChain = chain.Build(target);
 32
 033                    int count = 0;
 034                    foreach (X509ChainStatus status in chain.ChainStatus)
 035                    {
 036                        if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain) || status.Status.HasFlag(X509ChainS
 037                        {
 038                            osCanBuildChain = false;
 039                            break;
 40                        }
 41
 042                        count++;
 043                    }
 44
 045                    DisposeChainElements(chain);
 46
 47                    // OS failed to build the chain but we have at least some intermediates.
 48                    // We will try to add them to "Intermediate Certification Authorities" store.
 049                    if (!osCanBuildChain)
 050                    {
 051                        X509Store? store = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine);
 52
 53                        try
 054                        {
 055                            store.Open(OpenFlags.ReadWrite);
 056                        }
 057                        catch
 058                        {
 59                            // If using system store fails, try to fall-back to user store.
 060                            store.Dispose();
 061                            store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser);
 62                            try
 063                            {
 064                                store.Open(OpenFlags.ReadWrite);
 065                            }
 066                            catch
 067                            {
 068                                store.Dispose();
 069                                store = null;
 070                                if (NetEventSource.Log.IsEnabled())
 071                                {
 072                                    NetEventSource.Error(this, $"Failed to open certificate store for intermediates.");
 073                                }
 074                            }
 075                        }
 76
 077                        if (store != null)
 078                        {
 079                            using (store)
 080                            {
 81                                // Add everything except the root
 082                                for (int index = count; index < intermediates.Count - 1; index++)
 083                                {
 084                                    TryAddToStore(store, intermediates[index]);
 085                                }
 86
 087                                osCanBuildChain = chain.Build(target);
 088                                foreach (X509ChainStatus status in chain.ChainStatus)
 089                                {
 090                                    if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain) || status.Status.HasFla
 091                                    {
 092                                        osCanBuildChain = false;
 093                                        break;
 94                                    }
 095                                }
 96
 097                                DisposeChainElements(chain);
 98
 099                                if (!osCanBuildChain)
 0100                                {
 101                                    // Add also root to Intermediate CA store so OS can complete building chain.
 102                                    // (This does not make it trusted.)
 0103                                    TryAddToStore(store, intermediates[intermediates.Count - 1]);
 0104                                }
 0105                            }
 0106                        }
 0107                    }
 0108                }
 0109            }
 110
 0111            IntermediateCertificates = intermediates;
 0112            TargetCertificate = target;
 0113            Trust = trust;
 114
 115            static void TryAddToStore(X509Store store, X509Certificate2 certificate)
 0116            {
 117                try
 0118                {
 0119                    store.Add(certificate);
 0120                }
 0121                catch (CryptographicException ex)
 0122                {
 123                    // Continue even if we can't add certificates due to permission issues
 0124                    if (NetEventSource.Log.IsEnabled())
 0125                    {
 0126                        NetEventSource.Error(null, $"Failed to add certificate to store: {ex.Message}, certificate: {cer
 0127                    }
 0128                }
 0129            }
 130
 131            static void DisposeChainElements(X509Chain chain)
 0132            {
 0133                int elementsCount = chain.ChainElements.Count;
 0134                for (int i = 0; i < elementsCount; i++)
 0135                {
 0136                    chain.ChainElements[i].Certificate.Dispose();
 0137                }
 0138            }
 0139        }
 140    }
 141}
 142