TunableValidator.cs
using System; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; namespace Huddled.Net { public class TunableValidator { public static bool IgnoreChainErrors { get; set; } public static bool ShowConsoleStandardOutput { get; set; } private static readonly Dictionary<string, string> Trusted = new Dictionary<string, string>(); private static bool _allowNextCert; private static bool _addNextCert; private static string _lasthash; private static string _lasthost; public static void WriteOutToConsole(string msg) { if (ShowConsoleStandardOutput) { Console.Out.Write("{1:yyy-MM-dd HH:mm:ss} {0}{2}", msg, DateTime.Now, Environment.NewLine); } } public static void SetValidator(bool ignoreChainErrors = false, Hashtable trustedCerts = null, bool showConsoleStandardOutput = true) { IgnoreChainErrors = ignoreChainErrors; ShowConsoleStandardOutput = showConsoleStandardOutput; if (trustedCerts != null) { foreach (var cert in trustedCerts.Keys) { Trusted[cert.ToString()] = trustedCerts[cert].ToString(); } } ServicePointManager.ServerCertificateValidationCallback = TunableValidationCallback; } public static void ApproveLastRequest() { Trusted.Add(_lasthash, _lasthost); // Console.WriteLine(string.Format("Added \"{0}\"=\"{1}\"", _lasthash, _lasthost)); WriteOutToConsole("Added \"" + _lasthash + "\"=\"" + _lasthost + "\""); } public static void ApproveNextRequest(bool andTrustCert = false) { _allowNextCert = true; _addNextCert = andTrustCert; } public static Dictionary<string, string> TrustedCerts { get { return Trusted; } } private static bool TunableValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { bool result = true; var request = sender as WebRequest; // If there's no remote certificate, we're always going to fail (why are we even in here?) if (SslPolicyErrors.RemoteCertificateNotAvailable == (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable)) { result = false; } else { // If the certificate doesn't match the address (like Splunk's self-signed cert) if (SslPolicyErrors.RemoteCertificateNameMismatch == (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch)) { result = TrustValidate(certificate, request); } // If the CA isn't trusted, and IgnoreChainErrors isn't set if (!IgnoreChainErrors && (SslPolicyErrors.RemoteCertificateChainErrors == (sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors))) { result = TrustValidate(certificate, request); } } _lasthost = string.Empty; _lasthash = certificate.GetCertHashString(); if (_addNextCert) { _addNextCert = false; ApproveLastRequest(); result = true; } if (_allowNextCert) { _allowNextCert = false; result = true; } if (result) return true; Console.Error.WriteLine("Server SSL Certificate:"); if (request != null) { _lasthost = request.RequestUri.Host; Console.Error.WriteLine(" Server: " + _lasthost); } Console.Error.WriteLine(" Subject: " + certificate.Subject); Console.Error.WriteLine(" Hash: " + _lasthash); Console.Error.WriteLine("Effective: " + certificate.GetEffectiveDateString()); Console.Error.WriteLine(" Expires: " + certificate.GetExpirationDateString()); if (request == null) { Console.Error.WriteLine(" ? Sender: " + sender); } Console.Error.WriteLine(" Errors: " + sslPolicyErrors); Console.Error.WriteLine(" Rejected: To accept, use Add-SessionTrustedCertificate to map the certificate and hostmask. Use the -LastFailed parameter to do it automatically."); return false; } private static bool TrustValidate(X509Certificate certificate, WebRequest request = null) { string host; return request != null && Trusted.TryGetValue(certificate.GetCertHashString() ?? String.Empty, out host) && request.RequestUri.Host == host; } } } |