Skip to content

Commit be816a0

Browse files
author
Connor Lee
committed
Validate certificate domains match configuration before reuse
Previously, persisted certificates were reused based solely on expiry dates. If the configured domain changed, the old certificate would still be served. Now the validator checks the certificate's Subject Alternative Names against LetsEncryptOptions.Domains and triggers a renewal when they don't match.
1 parent 140d9a2 commit be816a0

1 file changed

Lines changed: 33 additions & 6 deletions

File tree

src/PingmanTools.AspNet.EncryptWeMust/Certificates/CertificateValidator.cs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Linq;
23
using System.Security.Cryptography;
4+
using System.Security.Cryptography.X509Certificates;
35
using Microsoft.Extensions.Logging;
46
using PingmanTools.AspNet.EncryptWeMust.Certes;
57

@@ -9,7 +11,7 @@ public interface ICertificateValidator
911
{
1012
bool IsCertificateValid(IAbstractCertificate certificate);
1113
}
12-
14+
1315
public class CertificateValidator : ICertificateValidator
1416
{
1517
private readonly LetsEncryptOptions _options;
@@ -29,21 +31,27 @@ public bool IsCertificateValid(IAbstractCertificate certificate)
2931
{
3032
if (certificate == null)
3133
return false;
32-
34+
3335
var now = DateTime.Now;
3436

3537
_logger.LogTrace("Validating cert UntilExpiry {UntilExpiry}, AfterIssue {AfterIssue} - {Certificate}",
3638
_options.TimeUntilExpiryBeforeRenewal, _options.TimeAfterIssueDateBeforeRenewal, certificate);
37-
39+
40+
if (certificate is LetsEncryptX509Certificate x509Cert && !DomainsCoverConfiguration(x509Cert))
41+
{
42+
_logger.LogInformation("Certificate domains do not match configured domains. A new certificate will be requested.");
43+
return false;
44+
}
45+
3846
if (_options.TimeUntilExpiryBeforeRenewal != null && certificate.NotAfter - now < _options.TimeUntilExpiryBeforeRenewal)
3947
return false;
40-
48+
4149
if (_options.TimeAfterIssueDateBeforeRenewal != null && now - certificate.NotBefore > _options.TimeAfterIssueDateBeforeRenewal)
4250
return false;
43-
51+
4452
if (certificate.NotBefore > now || certificate.NotAfter < now)
4553
return false;
46-
54+
4755
return true;
4856
}
4957
catch (CryptographicException exc)
@@ -52,5 +60,24 @@ public bool IsCertificateValid(IAbstractCertificate certificate)
5260
return false;
5361
}
5462
}
63+
64+
private bool DomainsCoverConfiguration(LetsEncryptX509Certificate certificate)
65+
{
66+
var configuredDomains = _options.Domains?.ToArray();
67+
if (configuredDomains == null || configuredDomains.Length == 0)
68+
return true;
69+
70+
var cert = certificate.GetCertificate();
71+
var sanExtension = cert.Extensions["2.5.29.17"] as X509SubjectAlternativeNameExtension;
72+
73+
if (sanExtension == null)
74+
return false;
75+
76+
var certDomains = sanExtension.EnumerateDnsNames().ToArray();
77+
78+
return configuredDomains.All(configured =>
79+
certDomains.Any(certDomain =>
80+
string.Equals(certDomain, configured, StringComparison.OrdinalIgnoreCase)));
81+
}
5582
}
5683
}

0 commit comments

Comments
 (0)