Skip to content

Commit

Permalink
Adapted certificate validation in WindowsCredential
Browse files Browse the repository at this point in the history
Adapted certificate validation in WindowsCredential to work around the absence of X509CertificateValidator.CreateChainTrustValidator in the .NET Standard and .NET Core flavors of WCF.
  • Loading branch information
danjagnow committed Mar 4, 2021
1 parent bd0cdcd commit b051e84
Showing 1 changed file with 67 additions and 2 deletions.
69 changes: 67 additions & 2 deletions Src/Contract/WindowsCredential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Text;

namespace SolarWinds.InformationService.Contract2
{
Expand Down Expand Up @@ -109,8 +110,12 @@ public override void ApplyTo(ChannelFactory channelFactory)

X509ChainPolicy chainPolicy = new X509ChainPolicy();
chainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority | X509VerificationFlags.IgnoreNotTimeValid;
// TODO - this is missing from .net standard, but it may not be possible to do Windows auth safely without it
//channelFactory.Credentials.ServiceCertificate.Authentication.CustomCertificateValidator = X509CertificateValidator.CreateChainTrustValidator(true, chainPolicy);

// Previously, this was calling X509CertificateValidator.CreateChainTrustValidator, but this is not available for .NET Standard and .NET Core.
// The CreateChainTrustValidator method returned a new ChainTrustValidator.
// The source for that class has been adapted and included as a private class below.
// https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System.IdentityModel/System/IdentityModel/Selectors/X509CertificateValidator.cs#L76
channelFactory.Credentials.ServiceCertificate.Authentication.CustomCertificateValidator = new ChainTrustValidator(chainPolicy);
}

public string GetPassword()
Expand Down Expand Up @@ -148,5 +153,65 @@ protected override void Dispose(bool disposing)

base.Dispose(disposing);
}

// Adapted from https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System.IdentityModel/System/IdentityModel/Selectors/X509CertificateValidator.cs#L188
// Several simplifications have been made because the constructor parameters are known.
private class ChainTrustValidator : X509CertificateValidator
{
private readonly X509ChainPolicy _chainPolicy;

public ChainTrustValidator(X509ChainPolicy chainPolicy)
{
_chainPolicy = chainPolicy ?? throw new ArgumentNullException(nameof(chainPolicy));
}

public override void Validate(X509Certificate2 certificate)
{
if (certificate == null)
{
throw new ArgumentNullException(nameof(certificate));
}

X509Chain chain = new X509Chain(true)
{
ChainPolicy = _chainPolicy
};

if (!chain.Build(certificate))
{
// In the .NET Framework source, this throws a SecurityTokenValidationException instead.
throw new InvalidOperationException($"The specific X.509 certificate chain building failed. The certificate that was used ({GetCertificateId(certificate)}) has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. Chain status: {GetChainStatusInformation(chain.ChainStatus)}");
}
}

private static string GetChainStatusInformation(X509ChainStatus[] chainStatus)
{
if (chainStatus != null)
{
StringBuilder error = new StringBuilder(128);

for (int i = 0; i < chainStatus.Length; ++i)
{
error.Append(chainStatus[i].StatusInformation);
error.Append(" ");
}
return error.ToString();
}

return string.Empty;
}

// From https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System.IdentityModel/System/IdentityModel/SecurityUtils.cs#L152
private static string GetCertificateId(X509Certificate2 certificate)
{
string certificateId = certificate.SubjectName.Name;
if (string.IsNullOrEmpty(certificateId))
{
certificateId = certificate.Thumbprint;
}

return certificateId;
}
}
}
}

0 comments on commit b051e84

Please sign in to comment.