diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs
index 491eb5adb974..1d8c713a1e88 100644
--- a/src/Shared/CertificateGeneration/CertificateManager.cs
+++ b/src/Shared/CertificateGeneration/CertificateManager.cs
@@ -9,6 +9,9 @@
 using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
 
 #nullable enable
 
@@ -16,7 +19,8 @@ namespace Microsoft.AspNetCore.Certificates.Generation;
 
 internal abstract class CertificateManager
 {
-    internal const int CurrentAspNetCoreCertificateVersion = 2;
+    internal const int CurrentAspNetCoreCertificateVersion = 3;
+    internal const int CurrentMinimumAspNetCoreCertificateVersion = 3;
 
     // OID used for HTTPS certs
     internal const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1";
@@ -24,7 +28,12 @@ internal abstract class CertificateManager
 
     private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1";
     private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication";
+    
+    // dns names of the host from a container
+    private const string LocalHostDockerHttpsDnsName = "host.docker.internal";
+    private const string ContainersDockerHttpsDnsName = "host.containers.internal";
 
+    // main cert subject
     private const string LocalhostHttpsDnsName = "localhost";
     internal const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName;
 
@@ -46,7 +55,28 @@ public int AspNetHttpsCertificateVersion
     {
         get;
         // For testing purposes only
-        internal set;
+        internal set
+        {
+            ArgumentOutOfRangeException.ThrowIfLessThan(
+                value,
+                MinimumAspNetHttpsCertificateVersion,
+                $"{nameof(AspNetHttpsCertificateVersion)} cannot be lesser than {nameof(MinimumAspNetHttpsCertificateVersion)}");
+            field = value;
+        }
+    }
+
+    public int MinimumAspNetHttpsCertificateVersion
+    {
+        get;
+        // For testing purposes only
+        internal set
+        {
+            ArgumentOutOfRangeException.ThrowIfGreaterThan(
+                value,
+                AspNetHttpsCertificateVersion,
+                $"{nameof(MinimumAspNetHttpsCertificateVersion)} cannot be greater than {nameof(AspNetHttpsCertificateVersion)}");
+            field = value;
+        }
     }
 
     public string Subject { get; }
@@ -57,9 +87,16 @@ public CertificateManager() : this(LocalhostHttpsDistinguishedName, CurrentAspNe
 
     // For testing purposes only
     internal CertificateManager(string subject, int version)
+        : this(subject, version, version)
+    {
+    }
+
+    // For testing purposes only
+    internal CertificateManager(string subject, int generatedVersion, int minimumVersion)
     {
         Subject = subject;
-        AspNetHttpsCertificateVersion = version;
+        AspNetHttpsCertificateVersion = generatedVersion;
+        MinimumAspNetHttpsCertificateVersion = minimumVersion;
     }
 
     /// <remarks>
@@ -147,30 +184,30 @@ bool HasOid(X509Certificate2 certificate, string oid) =>
             certificate.Extensions.OfType<X509Extension>()
                 .Any(e => string.Equals(oid, e.Oid?.Value, StringComparison.Ordinal));
 
-        static byte GetCertificateVersion(X509Certificate2 c)
-        {
-            var byteArray = c.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];
-            }
-        }
-
         bool IsValidCertificate(X509Certificate2 certificate, DateTimeOffset currentDate, bool requireExportable) =>
             certificate.NotBefore <= currentDate &&
             currentDate <= certificate.NotAfter &&
             (!requireExportable || IsExportable(certificate)) &&
-            GetCertificateVersion(certificate) >= AspNetHttpsCertificateVersion;
+            GetCertificateVersion(certificate) >= MinimumAspNetHttpsCertificateVersion;
+    }
+
+    internal static byte GetCertificateVersion(X509Certificate2 c)
+    {
+        var byteArray = c.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];
+        }
     }
 
     protected virtual void PopulateCertificatesFromStore(X509Store store, List<X509Certificate2> certificates, bool requireExportable)
@@ -487,7 +524,7 @@ public void CleanupHttpsCertificates()
     /// <remarks>Implementations may choose to throw, rather than returning <see cref="TrustLevel.None"/>.</remarks>
     protected abstract TrustLevel TrustCertificateCore(X509Certificate2 certificate);
 
-    protected abstract bool IsExportable(X509Certificate2 c);
+    internal abstract bool IsExportable(X509Certificate2 c);
 
     protected abstract void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate);
 
@@ -649,6 +686,8 @@ internal X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOf
         var extensions = new List<X509Extension>();
         var sanBuilder = new SubjectAlternativeNameBuilder();
         sanBuilder.AddDnsName(LocalhostHttpsDnsName);
+        sanBuilder.AddDnsName(LocalHostDockerHttpsDnsName);
+        sanBuilder.AddDnsName(ContainersDockerHttpsDnsName);
 
         var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, critical: true);
         var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(
diff --git a/src/Shared/CertificateGeneration/MacOSCertificateManager.cs b/src/Shared/CertificateGeneration/MacOSCertificateManager.cs
index a38e22762190..36b0c92d895c 100644
--- a/src/Shared/CertificateGeneration/MacOSCertificateManager.cs
+++ b/src/Shared/CertificateGeneration/MacOSCertificateManager.cs
@@ -302,7 +302,7 @@ private static bool IsCertOnKeychain(string keychain, X509Certificate2 certifica
     }
 
     // We don't have a good way of checking on the underlying implementation if it is exportable, so just return true.
-    protected override bool IsExportable(X509Certificate2 c) => true;
+    internal override bool IsExportable(X509Certificate2 c) => true;
 
     protected override X509Certificate2 SaveCertificateCore(X509Certificate2 certificate, StoreName storeName, StoreLocation storeLocation)
     {
diff --git a/src/Shared/CertificateGeneration/UnixCertificateManager.cs b/src/Shared/CertificateGeneration/UnixCertificateManager.cs
index 0ea92b87483e..149e0fab3ba6 100644
--- a/src/Shared/CertificateGeneration/UnixCertificateManager.cs
+++ b/src/Shared/CertificateGeneration/UnixCertificateManager.cs
@@ -179,7 +179,7 @@ internal override void CorrectCertificateState(X509Certificate2 candidate)
         // This is about correcting storage, not trust.
     }
 
-    protected override bool IsExportable(X509Certificate2 c) => true;
+    internal override bool IsExportable(X509Certificate2 c) => true;
 
     protected override TrustLevel TrustCertificateCore(X509Certificate2 certificate)
     {
diff --git a/src/Shared/CertificateGeneration/WindowsCertificateManager.cs b/src/Shared/CertificateGeneration/WindowsCertificateManager.cs
index 85a72be37c66..1cf1ebd9480e 100644
--- a/src/Shared/CertificateGeneration/WindowsCertificateManager.cs
+++ b/src/Shared/CertificateGeneration/WindowsCertificateManager.cs
@@ -27,7 +27,7 @@ internal WindowsCertificateManager(string subject, int version)
     {
     }
 
-    protected override bool IsExportable(X509Certificate2 c)
+    internal override bool IsExportable(X509Certificate2 c)
     {
 #if XPLAT
         // For the first run experience we don't need to know if the certificate can be exported.
diff --git a/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs b/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs
index bcc2d6ef05b5..37bdd2cafa0a 100644
--- a/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs
+++ b/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs
@@ -388,6 +388,7 @@ public void EnsureCreateHttpsCertificate_ReturnsExpiredCertificateIfVersionIsInc
         ListCertificates();
 
         _manager.AspNetHttpsCertificateVersion = 2;
+        _manager.MinimumAspNetHttpsCertificateVersion = 2;
 
         var httpsCertificateList = _manager.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true);
         Assert.Empty(httpsCertificateList);
@@ -400,17 +401,40 @@ public void EnsureCreateHttpsCertificate_ReturnsExpiredCertificateForEmptyVersio
 
         var now = DateTimeOffset.UtcNow;
         now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+        _manager.MinimumAspNetHttpsCertificateVersion = 0;
         _manager.AspNetHttpsCertificateVersion = 0;
         var creation = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, isInteractive: false);
         Output.WriteLine(creation.ToString());
         ListCertificates();
 
         _manager.AspNetHttpsCertificateVersion = 1;
+        _manager.MinimumAspNetHttpsCertificateVersion = 1;
 
         var httpsCertificateList = _manager.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true);
         Assert.Empty(httpsCertificateList);
     }
 
+    [Fact]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720", Queues = "All.OSX")]
+    public void EnsureCreateHttpsCertificate_DoNotOverrideValidOldCertificate()
+    {
+        _fixture.CleanupCertificates();
+
+        var now = DateTimeOffset.UtcNow;
+        now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+        var creation = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, isInteractive: false);
+        Output.WriteLine(creation.ToString());
+        ListCertificates();
+
+        // Simulate a tool with the same min version as the already existing cert but with a more
+        // recent generation version
+        _manager.MinimumAspNetHttpsCertificateVersion = 1;
+        _manager.AspNetHttpsCertificateVersion = 2;
+        var alreadyExist = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, isInteractive: false);
+        Output.WriteLine(alreadyExist.ToString());
+        Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, alreadyExist);
+    }
+
     [ConditionalFact]
     [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720", Queues = "All.OSX")]
     public void EnsureCreateHttpsCertificate_ReturnsValidIfVersionIsZero()
@@ -419,7 +443,7 @@ public void EnsureCreateHttpsCertificate_ReturnsValidIfVersionIsZero()
 
         var now = DateTimeOffset.UtcNow;
         now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
-        _manager.AspNetHttpsCertificateVersion = 0;
+        _manager.MinimumAspNetHttpsCertificateVersion = 0;
         var creation = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, isInteractive: false);
         Output.WriteLine(creation.ToString());
         ListCertificates();
@@ -441,7 +465,7 @@ public void EnsureCreateHttpsCertificate_ReturnValidIfCertIsNewer()
         Output.WriteLine(creation.ToString());
         ListCertificates();
 
-        _manager.AspNetHttpsCertificateVersion = 1;
+        _manager.MinimumAspNetHttpsCertificateVersion = 1;
         var httpsCertificateList = _manager.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true);
         Assert.NotEmpty(httpsCertificateList);
     }
@@ -455,16 +479,24 @@ public void ListCertificates_AlwaysReturnsTheCertificate_WithHighestVersion()
         var now = DateTimeOffset.UtcNow;
         now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
         _manager.AspNetHttpsCertificateVersion = 1;
+        _manager.MinimumAspNetHttpsCertificateVersion = 1;
         var creation = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, isInteractive: false);
         Output.WriteLine(creation.ToString());
         ListCertificates();
 
         _manager.AspNetHttpsCertificateVersion = 2;
+        _manager.MinimumAspNetHttpsCertificateVersion = 2;
         creation = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, isInteractive: false);
         Output.WriteLine(creation.ToString());
         ListCertificates();
 
-        _manager.AspNetHttpsCertificateVersion = 1;
+        _manager.AspNetHttpsCertificateVersion = 3;
+        _manager.MinimumAspNetHttpsCertificateVersion = 3;
+        creation = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, isInteractive: false);
+        Output.WriteLine(creation.ToString());
+        ListCertificates();
+
+        _manager.MinimumAspNetHttpsCertificateVersion = 2;
         var httpsCertificateList = _manager.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true);
         Assert.Equal(2, httpsCertificateList.Count);
 
@@ -475,13 +507,13 @@ public void ListCertificates_AlwaysReturnsTheCertificate_WithHighestVersion()
             firstCertificate.Extensions.OfType<X509Extension>(),
             e => e.Critical == false &&
                 e.Oid.Value == CertificateManager.AspNetHttpsOid &&
-                e.RawData[0] == 2);
+                e.RawData[0] == 3);
 
         Assert.Contains(
             secondCertificate.Extensions.OfType<X509Extension>(),
             e => e.Critical == false &&
                 e.Oid.Value == CertificateManager.AspNetHttpsOid &&
-                e.RawData[0] == 1);
+                e.RawData[0] == 2);
     }
 
     [ConditionalFact]
@@ -532,6 +564,8 @@ public CertFixture()
 
     internal void CleanupCertificates()
     {
+        Manager.MinimumAspNetHttpsCertificateVersion = 1;
+        Manager.AspNetHttpsCertificateVersion = 1;
         Manager.RemoveAllCertificates(StoreName.My, StoreLocation.CurrentUser);
         if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
         {
diff --git a/src/Tools/dotnet-dev-certs/src/Program.cs b/src/Tools/dotnet-dev-certs/src/Program.cs
index 222e3c355e57..bf6a9d964a5c 100644
--- a/src/Tools/dotnet-dev-certs/src/Program.cs
+++ b/src/Tools/dotnet-dev-certs/src/Program.cs
@@ -1,9 +1,13 @@
 // 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.Linq;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography.X509Certificates;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
 using Microsoft.AspNetCore.Certificates.Generation;
 using Microsoft.Extensions.CommandLineUtils;
 using Microsoft.Extensions.Tools.Internal;
@@ -110,6 +114,10 @@ public static int Main(string[] args)
                     "Display warnings and errors only.",
                     CommandOptionType.NoValue);
 
+                var checkJsonOutput = c.Option("--check-trust-machine-readable",
+                    "Same as running --check --trust, but output the results in json.",
+                    CommandOptionType.NoValue);
+
                 c.HelpOption("-h|--help");
 
                 c.OnExecute(() =>
@@ -122,9 +130,20 @@ public static int Main(string[] args)
                         listener.EnableEvents(CertificateManager.Log, System.Diagnostics.Tracing.EventLevel.Verbose);
                     }
 
+                    if (checkJsonOutput.HasValue())
+                    {
+                        if (exportPath.HasValue() || trust?.HasValue() == true || format.HasValue() || noPassword.HasValue() || check.HasValue() || clean.HasValue() ||
+                           (!import.HasValue() && password.HasValue()) || 
+                           (import.HasValue() && !password.HasValue()))
+                        {
+                            reporter.Error(InvalidUsageErrorMessage);
+                            return CriticalError;
+                        }
+                    }
+
                     if (clean.HasValue())
                     {
-                        if (exportPath.HasValue() || trust?.HasValue() == true || format.HasValue() || noPassword.HasValue() || check.HasValue() ||
+                        if (exportPath.HasValue() || trust?.HasValue() == true || format.HasValue() || noPassword.HasValue() || check.HasValue() || checkJsonOutput.HasValue() ||
                            (!import.HasValue() && password.HasValue()) ||
                            (import.HasValue() && !password.HasValue()))
                         {
@@ -135,7 +154,7 @@ public static int Main(string[] args)
 
                     if (check.HasValue())
                     {
-                        if (exportPath.HasValue() || password.HasValue() || noPassword.HasValue() || clean.HasValue() || format.HasValue() || import.HasValue())
+                        if (exportPath.HasValue() || password.HasValue() || noPassword.HasValue() || clean.HasValue() || format.HasValue() || import.HasValue() || checkJsonOutput.HasValue())
                         {
                             reporter.Error(InvalidUsageErrorMessage);
                             return CriticalError;
@@ -179,6 +198,11 @@ public static int Main(string[] args)
                         return ImportCertificate(import, password, reporter);
                     }
 
+                    if (checkJsonOutput.HasValue())
+                    {
+                        return CheckHttpsCertificateJsonOutput(reporter);
+                    }
+
                     return EnsureHttpsCertificate(exportPath, password, noPassword, trust, format, reporter);
                 });
             });
@@ -335,6 +359,16 @@ private static void ReportCertificates(IReporter reporter, IReadOnlyList<X509Cer
         });
     }
 
+    private static int CheckHttpsCertificateJsonOutput(IReporter reporter)
+    {
+        var availableCertificates = CertificateManager.Instance.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true);
+
+        var certReports = availableCertificates.Select(CertificateReport.FromX509Certificate2).ToList();
+        reporter.Output(JsonSerializer.Serialize(certReports, options: new JsonSerializerOptions { WriteIndented = true }));
+
+        return Success;
+    }
+
     private static int EnsureHttpsCertificate(CommandOption exportPath, CommandOption password, CommandOption noPassword, CommandOption trust, CommandOption exportFormat, IReporter reporter)
     {
         var now = DateTimeOffset.Now;
@@ -452,3 +486,63 @@ private static int EnsureHttpsCertificate(CommandOption exportPath, CommandOptio
         }
     }
 }
+
+/// <summary>
+/// A Serializable friendly version of the cert report output
+/// </summary>
+internal class CertificateReport
+{
+    public string Thumbprint { get; init; }
+    public string Subject { get; init; }
+    public List<string> X509SubjectAlternativeNameExtension { get; init; }
+    public int Version { get; init; }
+    public DateTime ValidityNotBefore { get; init; }
+    public DateTime ValidityNotAfter { get; init; }
+    public bool IsHttpsDevelopmentCertificate { get; init; }
+    public bool IsExportable { get; init; }
+    public string TrustLevel { get; private set; }
+
+    public static CertificateReport FromX509Certificate2(X509Certificate2 cert)
+    {
+        var certificateManager = CertificateManager.Instance;
+        var status = certificateManager.CheckCertificateState(cert);
+        string statusString;
+        if (!status.Success)
+        {
+            statusString = "Invalid";
+        }
+        else
+        {
+            var trustStatus = certificateManager.GetTrustLevel(cert);
+            statusString = trustStatus.ToString();
+        }
+        return new CertificateReport
+        {
+            Thumbprint = cert.Thumbprint,
+            Subject = cert.Subject,
+            X509SubjectAlternativeNameExtension = GetSanExtension(cert),
+            Version = CertificateManager.GetCertificateVersion(cert),
+            ValidityNotBefore = cert.NotBefore,
+            ValidityNotAfter = cert.NotAfter,
+            IsHttpsDevelopmentCertificate = CertificateManager.IsHttpsDevelopmentCertificate(cert),
+            IsExportable = certificateManager.IsExportable(cert),
+            TrustLevel = statusString
+        };
+
+        static List<string> GetSanExtension(X509Certificate2 cert)
+        {
+            var dnsNames = new List<string>();
+            foreach (var extension in cert.Extensions)
+            {
+                if (extension is X509SubjectAlternativeNameExtension sanExtension)
+                {
+                    foreach (var dns in sanExtension.EnumerateDnsNames())
+                    {
+                        dnsNames.Add(dns);
+                    }
+                }
+            }
+            return dnsNames;
+        }
+    }
+}