Add support for unstrusted certificates (#128)

This commit is contained in:
Flaminel authored and GitHub committed 2025-05-06 15:42:41 +03:00
1 parent 7d2bf41bec
commit 9463d7587f
9 files changed
+181 -2

No files matched your search

@@ -0,0 +1,55 @@
using System.Net;
using System.Net.Sockets;
namespace Infrastructure.Extensions;
public static class IpAddressExtensions
{
public static bool IsLocalAddress(this IPAddress ipAddress)
{
// Map back to IPv4 if mapped to IPv6, for example "::ffff:1.2.3.4" to "1.2.3.4".
if (ipAddress.IsIPv4MappedToIPv6)
{
ipAddress = ipAddress.MapToIPv4();
}
// Checks loopback ranges for both IPv4 and IPv6.
if (IPAddress.IsLoopback(ipAddress))
{
return true;
}
// IPv4
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
return IsLocalIPv4(ipAddress.GetAddressBytes());
}
// IPv6
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
return ipAddress.IsIPv6LinkLocal ||
ipAddress.IsIPv6UniqueLocal ||
ipAddress.IsIPv6SiteLocal;
}
return false;
}
private static bool IsLocalIPv4(byte[] ipv4Bytes)
{
// Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16)
bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
// Class A private range: 10.0.0.0 – 10.255.255.255 (10.0.0.0/8)
bool IsClassA() => ipv4Bytes[0] == 10;
// Class B private range: 172.16.0.0 – 172.31.255.255 (172.16.0.0/12)
bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
// Class C private range: 192.168.0.0 – 192.168.255.255 (192.168.0.0/16)
bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
}
}
@@ -0,0 +1,86 @@
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Common.Configuration.General;
using Common.Enums;
using Infrastructure.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Infrastructure.Services;
public class CertificateValidationService
{
private readonly ILogger<CertificateValidationService> _logger;
private readonly HttpConfig _config;
public CertificateValidationService(ILogger<CertificateValidationService> logger, IOptions<HttpConfig> config)
{
_logger = logger;
_config = config.Value;
}
public bool ShouldByPassValidationError(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
{
var targetHostName = string.Empty;
if (sender is not SslStream && sender is not string)
{
return true;
}
if (sender is SslStream request)
{
targetHostName = request.TargetHostName;
}
// Mailkit passes host in sender as string
if (sender is string stringHost)
{
targetHostName = stringHost;
}
if (certificate is X509Certificate2 cert2 && cert2.SignatureAlgorithm.FriendlyName == "md5RSA")
{
_logger.LogError(
$"https://{targetHostName} uses the obsolete md5 hash in its https certificate, if that is your certificate, please (re)create certificate with better algorithm as soon as possible.");
}
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}
if (targetHostName is "localhost" or "127.0.0.1")
{
return true;
}
var ipAddresses = GetIpAddresses(targetHostName);
if (_config.CertificateValidation == CertificateValidationType.Disabled)
{
return true;
}
if (_config.CertificateValidation == CertificateValidationType.DisabledForLocalAddresses &&
ipAddresses.All(i => i.IsLocalAddress()))
{
return true;
}
_logger.LogError($"certificate validation for {targetHostName} failed. {sslPolicyErrors}");
return false;
}
private static IPAddress[] GetIpAddresses(string host)
{
if (IPAddress.TryParse(host, out var ipAddress))
{
return [ipAddress];
}
return Dns.GetHostEntry(host).AddressList;
}
}