From 2a4f53885b46678ceb56a9bbb553a72baf8cf9a2 Mon Sep 17 00:00:00 2001 From: Rob Wood Date: Mon, 20 Jan 2025 14:15:26 +0000 Subject: [PATCH] Fix(smtp): Add SAN to SS certs that are generated to resolve #1610 (#1621) --- Rnwood.Smtp4dev.Tests/E2E/E2ETests.cs | 5 +++++ Rnwood.Smtp4dev.Tests/E2E/E2ETests_WebUI.cs | 20 ++++++++++++++------ Rnwood.Smtp4dev/Server/CertificateHelper.cs | 5 ++++- Rnwood.Smtp4dev/Server/SSCertGenerator.cs | 5 +++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Rnwood.Smtp4dev.Tests/E2E/E2ETests.cs b/Rnwood.Smtp4dev.Tests/E2E/E2ETests.cs index c16fa4c57..d6553cca5 100644 --- a/Rnwood.Smtp4dev.Tests/E2E/E2ETests.cs +++ b/Rnwood.Smtp4dev.Tests/E2E/E2ETests.cs @@ -99,6 +99,11 @@ protected void RunE2ETest(Action test, E2ETestOptions options = args.Add("--smtpport=0"); } + if (!args.Any(a => a.StartsWith("--hostname"))) + { + args.Add("--hostname=localhost"); + } + if (!args.Any(a => a.StartsWith("--smtpport"))) { args.Add("--smtpport=0"); diff --git a/Rnwood.Smtp4dev.Tests/E2E/E2ETests_WebUI.cs b/Rnwood.Smtp4dev.Tests/E2E/E2ETests_WebUI.cs index 1b93b32db..f3d1b9f3d 100644 --- a/Rnwood.Smtp4dev.Tests/E2E/E2ETests_WebUI.cs +++ b/Rnwood.Smtp4dev.Tests/E2E/E2ETests_WebUI.cs @@ -2,6 +2,7 @@ using MailKit.Security; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using MimeKit; +using MimeKit.Cryptography; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Support.Extensions; @@ -10,6 +11,8 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; using System.Threading; using WebDriverManager; using WebDriverManager.DriverConfigs.Impl; @@ -35,18 +38,18 @@ public void CheckMessageIsReceivedAndDisplayed(string basePath, bool inMemoryDb) RunUITest($"{nameof(CheckMessageIsReceivedAndDisplayed)}-{basePath}-{inMemoryDb}", (browser, baseUrl, smtpPortNumber) => { browser.Navigate().GoToUrl(baseUrl); - HomePage homePage = new HomePage(browser); + var homePage = new HomePage(browser); HomePage.MessageListControl messageList = WaitFor(() => homePage.MessageList); Assert.NotNull(messageList); string messageSubject = Guid.NewGuid().ToString(); - using (SmtpClient smtpClient = new SmtpClient()) + using (var smtpClient = new SmtpClient()) { smtpClient.SslProtocols = System.Security.Authentication.SslProtocols.Tls12; - smtpClient.ServerCertificateValidationCallback = (s, c, h, e) => true; + smtpClient.ServerCertificateValidationCallback = GetCertvalidationCallbackHandler(); smtpClient.CheckCertificateRevocation = false; - MimeMessage message = new MimeMessage(); + var message = new MimeMessage(); message.To.Add(MailboxAddress.Parse("to@to.com")); message.From.Add(MailboxAddress.Parse("from@from.com")); @@ -62,7 +65,7 @@ public void CheckMessageIsReceivedAndDisplayed(string basePath, bool inMemoryDb) smtpClient.Disconnect(true, new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token); } - HomePage.Grid.GridRow messageRow = WaitFor(() => messageList.Grid?.Rows?.SingleOrDefault()); + HomePage.Grid.GridRow messageRow = WaitFor(() => (messageList.Grid?.Rows?.SingleOrDefault())); Assert.NotNull(messageRow); Assert.Contains(messageRow.Cells, c => c.Text.Contains(messageSubject)); @@ -73,6 +76,11 @@ public void CheckMessageIsReceivedAndDisplayed(string basePath, bool inMemoryDb) }); } + private static RemoteCertificateValidationCallback GetCertvalidationCallbackHandler() + { + return (s, c, h, e) => new X509Certificate2(c.GetRawCertData()).GetSubjectDnsNames().Contains("localhost"); + } + [Fact] public void CheckUTF8MessageIsReceivedAndDisplayed() { @@ -88,7 +96,7 @@ public void CheckUTF8MessageIsReceivedAndDisplayed() using (SmtpClient smtpClient = new SmtpClient() { }) { smtpClient.SslProtocols = System.Security.Authentication.SslProtocols.None; - smtpClient.ServerCertificateValidationCallback = (s, c, h, e) => true; + smtpClient.ServerCertificateValidationCallback = GetCertvalidationCallbackHandler(); smtpClient.CheckCertificateRevocation = false; MimeMessage message = new MimeMessage(); diff --git a/Rnwood.Smtp4dev/Server/CertificateHelper.cs b/Rnwood.Smtp4dev/Server/CertificateHelper.cs index 9af1692a1..ccd8dc943 100644 --- a/Rnwood.Smtp4dev/Server/CertificateHelper.cs +++ b/Rnwood.Smtp4dev/Server/CertificateHelper.cs @@ -1,8 +1,10 @@ using System; using System.IO; +using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; +using MimeKit.Cryptography; using Rnwood.Smtp4dev.Server.Settings; using Serilog; @@ -66,7 +68,8 @@ public static X509Certificate2 GetTlsCertificate(ServerOptions options, ILogger X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); if (cert.Subject != $"CN={options.HostName}" || - DateTime.Parse(cert.GetExpirationDateString()) < DateTime.Now.AddDays(30)) + DateTime.Parse(cert.GetExpirationDateString()) < DateTime.Now.AddDays(30) + || !cert.GetSubjectDnsNames().Contains(options.HostName)) { cert = null; } diff --git a/Rnwood.Smtp4dev/Server/SSCertGenerator.cs b/Rnwood.Smtp4dev/Server/SSCertGenerator.cs index 5582a23a4..8a7541498 100644 --- a/Rnwood.Smtp4dev/Server/SSCertGenerator.cs +++ b/Rnwood.Smtp4dev/Server/SSCertGenerator.cs @@ -12,6 +12,8 @@ using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Asn1; +using System.Collections; [assembly: InternalsVisibleTo("Rnwood.Smtp4dev.Tests")] namespace Rnwood.Smtp4dev.Server @@ -27,6 +29,9 @@ public static X509Certificate2 CreateSelfSignedCertificate(string hostname) X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator(); certGenerator.SetSubjectDN(new X509Name("CN=" + hostname)); certGenerator.SetIssuerDN(new X509Name("CN=" + hostname)); + GeneralNames subjectAltNames = new GeneralNames(new GeneralName(GeneralName.DnsName, hostname)); + + certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltNames); BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random); certGenerator.SetSerialNumber(serialNumber);