File: src\Aspire.Hosting\Utils\X509Certificate2Extensions.cs
Web Access
Project: src\tests\Aspire.Playground.Tests\Aspire.Playground.Tests.csproj (Aspire.Playground.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Security.Cryptography.X509Certificates;
 
namespace Aspire.Hosting.Utils;
 
/// <summary>
/// Extension methods for <see cref="X509Certificate2Collection"/>.
/// </summary>
internal static class X509Certificate2Extensions
{
    // OID and friendly name for the ASP.NET Core HTTPS development certificate
    // See: https://github.com/dotnet/aspnetcore/blob/9f255d1ed217c0081e9b602fa86411b1821c2e0f/src/Shared/CertificateGeneration/CertificateManager.cs#L26-L27
    internal const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1";
    internal const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate";
 
    internal const int MinimumCertificateVersionSupportingContainerTrust = 4; // The minimum version of the ASP.NET Core certificate with SAN support for container domains
 
    /// <summary>
    /// Determines if the specified certificate is an ASP.NET Core development certificate.
    /// </summary>
    /// <param name="certificate">The <see cref="X509Certificate2"/> to check.</param>
    /// <returns>True if the certificate is an ASP.NET Core development certificate; otherwise, false.</returns>
    public static bool IsAspNetCoreDevelopmentCertificate(this X509Certificate2 certificate)
    {
        ArgumentNullException.ThrowIfNull(certificate);
 
        foreach (var extension in certificate.Extensions.OfType<X509Extension>())
        {
            if (string.Equals(AspNetHttpsOid, extension.Oid?.Value, StringComparison.Ordinal))
            {
                return true;
            }
        }
        return false;
    }
 
    /// <summary>
    /// Gets the version of the ASP.NET Core development certificate.
    /// </summary>
    /// <param name="certificate">The certificate to get the version from.</param>
    /// <returns>The version of the certificate (or 0 if no version is set).</returns>
    public static byte GetCertificateVersion(this X509Certificate2 certificate)
    {
        ArgumentNullException.ThrowIfNull(certificate);
 
        var byteArray = certificate.Extensions.OfType<X509Extension>()
            .Where(e => string.Equals(AspNetHttpsOid, e.Oid?.Value, StringComparison.Ordinal))
            .Single()
            .RawData;
 
        if ((byteArray.Length == AspNetHttpsOidFriendlyName.Length && byteArray[0] == (byte)'A') || byteArray.Length == 0)
        {
            // No Version set, default to 0
            return 0b0;
        }
        else
        {
            // Version is in the only byte of the byte array.
            return byteArray[0];
        }
    }
 
    /// <summary>
    /// Indicates that this is an ASP.NET Core development certificate with SAN support for common container domains.
    /// </summary>
    /// <param name="certificate">The certificate to check.</param>
    /// <returns>True if the certificate supports CA trust scenarios in containers; otherwise, false.</returns>
    public static bool SupportsContainerTrust(this X509Certificate2 certificate)
    {
        ArgumentNullException.ThrowIfNull(certificate);
 
        if (certificate.GetCertificateVersion() < MinimumCertificateVersionSupportingContainerTrust)
        {
            return false;
        }
 
        return true;
    }
}