File: System\Security\Cryptography\X509Certificates\OpenSslCertificateAssetDownloader.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using OpenSslX509ChainEventSource = System.Security.Cryptography.X509Certificates.OpenSslX509ChainEventSource;
 
namespace System.Security.Cryptography.X509Certificates
{
    internal static class OpenSslCertificateAssetDownloader
    {
        internal static X509Certificate2? DownloadCertificate(string uri, TimeSpan downloadTimeout)
        {
            byte[]? data = DownloadAsset(uri, downloadTimeout);
 
            if (data == null || data.Length == 0)
            {
                return null;
            }
 
            try
            {
                X509ContentType contentType = X509Certificate2.GetCertContentType(data);
                X509Certificate2 certificate;
 
                switch (contentType)
                {
                    case X509ContentType.Cert:
                        certificate = X509CertificateLoader.LoadCertificate(data);
                        break;
                    case X509ContentType.Pkcs7:
#pragma warning disable SYSLIB0057 // Content is known to be PKCS7.
                        certificate = new X509Certificate2(data);
#pragma warning restore SYSLIB0057
                        break;
                    default:
                        return null;
                }
 
                certificate.ThrowIfInvalid();
                return certificate;
            }
            catch (CryptographicException)
            {
                if (OpenSslX509ChainEventSource.Log.IsEnabled())
                {
                    OpenSslX509ChainEventSource.Log.InvalidDownloadedCertificate();
                }
 
                return null;
            }
        }
 
        internal static SafeX509CrlHandle? DownloadCrl(string uri, TimeSpan downloadTimeout)
        {
            byte[]? data = DownloadAsset(uri, downloadTimeout);
 
            if (data == null)
            {
                return null;
            }
 
            // DER-encoded CRL seems to be the most common off of some random spot-checking, so try DER first.
            SafeX509CrlHandle handle = Interop.Crypto.DecodeX509Crl(data, data.Length);
 
            if (!handle.IsInvalid)
            {
                return handle;
            }
 
            handle.Dispose();
 
            using (SafeBioHandle bio = Interop.Crypto.CreateMemoryBio())
            {
                Interop.Crypto.CheckValidOpenSslHandle(bio);
 
                Interop.Crypto.BioWrite(bio, data, data.Length);
 
                handle = Interop.Crypto.PemReadBioX509Crl(bio);
 
                // DecodeX509Crl failed, so we need to clear its error.
                // If PemReadBioX509Crl failed, clear that too.
                Interop.Crypto.ErrClearError();
 
                if (!handle.IsInvalid)
                {
                    return handle;
                }
 
                handle.Dispose();
            }
 
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.InvalidDownloadedCrl();
            }
 
            return null;
        }
 
        internal static SafeOcspResponseHandle? DownloadOcspGet(string uri, TimeSpan downloadTimeout)
        {
            byte[]? data = DownloadAsset(uri, downloadTimeout);
 
            if (data == null)
            {
                return null;
            }
 
            // https://tools.ietf.org/html/rfc6960#appendix-A.2 says that the response is the DER-encoded
            // response, so no rebuffering to interpret PEM is required.
            SafeOcspResponseHandle resp = Interop.Crypto.DecodeOcspResponse(data);
 
            if (resp.IsInvalid)
            {
                // We're not going to report this error to a user, so clear it
                // (to avoid tainting future exceptions)
                Interop.Crypto.ErrClearError();
 
                if (OpenSslX509ChainEventSource.Log.IsEnabled())
                {
                    OpenSslX509ChainEventSource.Log.InvalidDownloadedOcsp();
                }
            }
 
            return resp;
        }
 
        private static byte[]? DownloadAsset(string uri, TimeSpan downloadTimeout)
        {
            return System.Net.Http.X509ResourceClient.DownloadAsset(uri, downloadTimeout);
        }
    }
}
 
namespace System.Net.Http
{
    internal partial class X509ResourceClient
    {
        static partial void ReportNoClient()
        {
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.HttpClientNotAvailable();
            }
        }
 
        static partial void ReportNegativeTimeout()
        {
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.DownloadTimeExceeded();
            }
        }
 
        static partial void ReportDownloadStart(long totalMillis, string uri)
        {
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.AssetDownloadStart(totalMillis, uri);
            }
        }
 
        static partial void ReportDownloadStop(int bytesDownloaded)
        {
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.AssetDownloadStop(bytesDownloaded);
            }
        }
 
        static partial void ReportRedirectsExceeded()
        {
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.DownloadRedirectsExceeded();
            }
        }
 
        static partial void ReportRedirected(Uri newUri)
        {
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.DownloadRedirected(newUri);
            }
        }
 
        static partial void ReportRedirectNotFollowed(Uri redirectUri)
        {
            if (OpenSslX509ChainEventSource.Log.IsEnabled())
            {
                OpenSslX509ChainEventSource.Log.DownloadRedirectNotFollowed(redirectUri);
            }
        }
    }
}