Sfoglia il codice sorgente

Refresh HTTPS certificate when domain names change.

Kenric Nugteren 2 anni fa
parent
commit
1d4c9e725e

+ 49 - 1
prs.server/Engines/Certificate/CertificateEngine.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Formats.Asn1;
 using System.IO;
 using System.Linq;
 using System.Reflection;
@@ -220,7 +221,6 @@ namespace PRSServer
             Logger.Send(LogType.Information, "", "HTTPS Refresh Complete!");
         }
 
-
         private void DoTheWork()
         {
             try
@@ -599,6 +599,13 @@ namespace PRSServer
                 RefreshCertificateFile(CancellationToken.None);
                 return;
             }
+            var names = GetDnsNames(Certificate).ToHashSet();
+            var requiredNames = Properties.DomainNames.Split(',');
+            if(requiredNames.Any(x => !names.Contains(x)))
+            {
+                RefreshCertificateFile(CancellationToken.None);
+                return;
+            }
 
             Logger.Send(LogType.Information, "", "Refresh not required!");
         }
@@ -619,6 +626,47 @@ namespace PRSServer
             host?.Stop();
         }
 
+        public static IEnumerable<string> DnsAlternateNames(X509Certificate2 certificate)
+        {
+            // Adapted from https://stackoverflow.com/questions/16698307/how-do-you-parse-the-subject-alternate-names-from-an-x509certificate2/59382929#59382929
+
+            // OID for SubjectAlternativeName X509 extension.
+            const string SAN_OID = "2.5.29.17";
+
+            var extension = certificate.Extensions[SAN_OID];
+            if (extension is null) yield break;
+
+            // Tag value "2" is defined by:
+            //
+            //    dNSName                         [2]     IA5String,
+            //
+            // in: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
+            var dnsNameTag = new Asn1Tag(TagClass.ContextSpecific, tagValue: 2, isConstructed: false);
+
+            var asnReader = new AsnReader(extension.RawData, AsnEncodingRules.BER);
+            var sequenceReader = asnReader.ReadSequence(Asn1Tag.Sequence);
+
+            while (sequenceReader.HasData)
+            {
+                var tag = sequenceReader.PeekTag();
+                if(tag != dnsNameTag)
+                {
+                    sequenceReader.ReadEncodedValue();
+                    continue;
+                }
+
+                var dnsName = sequenceReader.ReadCharacterString(UniversalTagNumber.IA5String, dnsNameTag);
+                yield return dnsName;
+            }
+        }
+
+        public static IEnumerable<string> GetDnsNames(X509Certificate2 certificate)
+        {
+            yield return certificate.GetNameInfo(X509NameType.DnsName, false);
+            foreach (var name in DnsAlternateNames(certificate))
+                yield return name;
+        }
+
         #region File Names
 
         public static string CertificateFolder { get; }

+ 2 - 2
prs.server/Engines/Database/DatabaseEngine.cs

@@ -232,8 +232,8 @@ namespace PRSServer
                     {
                         RestListener.InitCertificate((ushort)Properties.Port, certificate);
 
-                        var names = certificate.GetNameInfo(X509NameType.DnsName, false);
-                        Logger.Send(LogType.Information, "", $"Certificate valid for {names}");
+                        var names = CertificateEngine.GetDnsNames(certificate);
+                        Logger.Send(LogType.Information, "", $"Certificate valid for {string.Join(',', names)}");
                         useHTTP = false;
                     }
                     else