refactor ssl certificate loading into common helper

This commit is contained in:
Tamer Wahba 2026-05-02 17:23:54 -04:00
parent 29e9d2992e
commit 5de31f51f5
No known key found for this signature in database
GPG key ID: B934B3FE68CD2A72
3 changed files with 44 additions and 35 deletions

View file

@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace NzbDrone.Common.Http
{
public class SslCertificateLoadException : Exception
{
public SslCertificateLoadException(string message)
: base(message)
{
}
}
public static class SslCertificateLoader
{
public static SslStreamCertificateContext LoadCertificateContext(string certPath, string certPassword)
{
var certificateCollection = new X509Certificate2Collection();
certificateCollection.Import(certPath, certPassword, X509KeyStorageFlags.DefaultKeySet);
var leafCert = certificateCollection.FirstOrDefault(c => c.HasPrivateKey);
if (leafCert == null)
{
throw new SslCertificateLoadException(
$"The SSL certificate file {certPath} does not contain a certificate with an associated private key");
}
return SslStreamCertificateContext.Create(leafCert, certificateCollection, offline: true);
}
}
}

View file

@ -2,11 +2,9 @@
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using DryIoc;
using DryIoc.Microsoft.DependencyInjection;
@ -21,6 +19,7 @@
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Exceptions;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Options;
@ -282,11 +281,11 @@ private static string BuildUrl(string scheme, string bindAddress, int port)
private static SslStreamCertificateContext ValidateSslCertificate(string cert, string password)
{
var certificateCollection = new X509Certificate2Collection();
SslStreamCertificateContext certificateContext;
try
{
certificateCollection.Import(cert, password, X509KeyStorageFlags.DefaultKeySet);
certificateContext = SslCertificateLoader.LoadCertificateContext(cert, password);
}
catch (CryptographicException ex)
{
@ -299,17 +298,7 @@ private static SslStreamCertificateContext ValidateSslCertificate(string cert, s
throw new RadarrStartupException(ex);
}
var leafCert = certificateCollection.FirstOrDefault(c => c.HasPrivateKey);
if (leafCert == null)
{
throw new RadarrStartupException(
$"The SSL certificate file {cert} does not contain a certificate with an associated private key");
}
certificateCollection.Remove(leafCert);
return SslStreamCertificateContext.Create(leafCert, certificateCollection, offline: true);
return certificateContext;
}
}
}

View file

@ -1,9 +1,8 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System;
using FluentValidation;
using FluentValidation.Validators;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation;
namespace Radarr.Api.V3.Config
@ -34,13 +33,13 @@ protected override bool IsValid(PropertyValidatorContext context)
return true;
}
var certificateCollection = new X509Certificate2Collection();
try
{
certificateCollection.Import(resource.SslCertPath, resource.SslCertPassword, X509KeyStorageFlags.DefaultKeySet);
SslCertificateLoader.LoadCertificateContext(resource.SslCertPath, resource.SslCertPassword);
return true;
}
catch (CryptographicException ex)
catch (Exception ex)
{
Logger.Debug(ex, "Invalid SSL certificate file or password. {0}", ex.Message);
@ -48,19 +47,6 @@ protected override bool IsValid(PropertyValidatorContext context)
return false;
}
if (certificateCollection.None(c => c.HasPrivateKey))
{
var message = $"The SSL certificate file {resource.SslCertPath} does not contain a certificate with an associated private key";
Logger.Debug($"{message}");
context.MessageFormatter.AppendArgument("message", message);
return false;
}
return true;
}
}
}