J'essaie de trouver un moyen d'ignorer la vérification du certificat lorsque vous demandez une ressource Https. Jusqu'à présent, j'ai trouvé un article utile sur Internet.
Mais j'ai encore un problème. S'il vous plaît examiner mon code. Je ne comprends tout simplement pas ce que signifie le code ServicePointManager.ServerCertificateValidationCallback
.
Quand cette méthode déléguée sera-t-elle appelée? Et encore une question, à quel endroit devrais-je écrire ce code? Avant ServicePointManager.ServerCertificateValidationCallback
exécuter ou avant Stream stream = request.GetRequestStream()
?
public HttpWebRequest GetRequest()
{
CookieContainer cookieContainer = new CookieContainer();
// Create a request to the server
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_remoteUrl);
#region Set request parameters
request.Method = _context.Request.HttpMethod;
request.UserAgent = _context.Request.UserAgent;
request.KeepAlive = true;
request.CookieContainer = cookieContainer;
request.PreAuthenticate = true;
request.AllowAutoRedirect = false;
#endregion
// For POST, write the post data extracted from the incoming request
if (request.Method == "POST")
{
Stream clientStream = _context.Request.InputStream;
request.ContentType = _context.Request.ContentType;
request.ContentLength = clientStream.Length;
ServicePointManager.ServerCertificateValidationCallback = delegate(
Object obj, X509Certificate certificate, X509Chain chain,
SslPolicyErrors errors)
{
return (true);
};
Stream stream = request.GetRequestStream();
....
}
....
return request;
}
}
Puisqu'il n'y a qu'un seul global ServicePointManager , définir ServicePointManager.ServerCertificateValidationCallback donnera le résultat selon lequel toutes les demandes suivantes hériteront de cette stratégie. Comme il s'agit d'un "paramètre" global, il serait préférable de le définir dans la méthode Application_Start de Global.asax .
La définition du rappel annule le comportement par défaut et vous pouvez créer vous-même une routine de validation personnalisée.
Pour toute personne intéressée à appliquer cette solution au cas par cas, il s'agit d'une option utilisant une expression Lambda. La même expression Lambda peut également être appliquée au filtre global mentionné par blak3r. Cette méthode semble nécessiter .NET 4.5.
String url = "https://www.stackoverflow.com";
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
Dans .NET 4.0, l'expression Lambda peut être appliquée au filtre global en tant que telle
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
Cela a fonctionné pour moi:
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true; // **** Always accept
};
Extrait d'ici: http://www.west-wind.com/weblog/posts/2011/Feb/11/HttpWebRequest-and-Ignoring-SSL-Certificate-Errors
Il y a aussi la solution de délégué courte:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Il a été mentionné qu'avant .NET 4.5, la propriété de la demande d'accès à sa ServicePointManager
n'était pas disponible.
Voici le code .NET 4.0 qui vous donnera accès à ServicePoint
au cas par cas. Cela ne vous donne pas accès au rappel sur demande, mais devrait vous permettre de trouver plus de détails sur le problème. Accédez simplement aux propriétés scvPoint.Certificate
(ou ClientCertificate
si vous préférez).
WebRequest request = WebRequest.Create(uri);
// oddity: these two .Address values are not necessarily the same!
// The service point appears to be related to the .Host, not the Uri itself.
// So, check the .Host vlaues before fussing in the debugger.
//
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
if (null != svcPoint)
{
if (!request.RequestUri.Host.Equals(svcPoint.Address.Host, StringComparison.OrdinalIgnoreCase))
{
Debug.WriteLine(".Address == " + request.RequestUri.ToString());
Debug.WriteLine(".ServicePoint.Address == " + svcPoint.Address.ToString());
}
Debug.WriteLine(".IssuerName == " + svcPoint.Certificate.GetIssuerName());
}
Incidemment, c’est le moyen le moins explicite de désactiver la validation des certificats dans une application donnée que je connais:
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
Basé sur la réponse d'Adam et le commentaire de Rob, j'ai utilisé ceci:
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => certificate.Issuer == "CN=localhost";
qui filtre le "ignorer" un peu. Bien sûr, d’autres émetteurs peuvent être ajoutés… .. Cela a été testé dans .NET 2.0 car nous devons prendre en charge certains codes hérités.
Exprimé explicitement ...
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(CertCheck);
private static bool CertCheck(object sender, X509Certificate cert,
X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
return true;
}
CA5386: Les outils d’analyse de vulnérabilité vous avertissent de ces codes.
Code correct:
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
return (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != SslPolicyErrors.RemoteCertificateNotAvailable;
};
Plutôt que d'ajouter un rappel à ServicePointManager qui remplacera globalement la validation de certificat, vous pouvez définir le rappel sur une instance locale de HttpClient. Cette approche ne devrait affecter que les appels effectués à l'aide de cette instance de HttpClient.
Voici un exemple de code montrant comment ignorer les erreurs de validation de certificat pour des serveurs spécifiques peut être implémenté dans un contrôleur API Web.
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public class MyController : ApiController
{
// use this HttpClient instance when making calls that need cert errors suppressed
private static readonly HttpClient httpClient;
static MyController()
{
// create a separate handler for use in this controller
var handler = new HttpClientHandler();
// add a custom certificate validation callback to the handler
handler.ServerCertificateCustomValidationCallback = ((sender, cert, chain, errors) => ValidateCert(sender, cert, chain, errors));
// create an HttpClient that will use the handler
httpClient = new HttpClient(handler);
}
protected static ValidateCert(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
{
// set a list of servers for which cert validation errors will be ignored
var overrideCerts = new string[]
{
"myproblemserver",
"someotherserver",
"localhost"
};
// if the server is in the override list, then ignore any validation errors
var serverName = cert.Subject.ToLower();
if (overrideCerts.Any(overrideName => serverName.Contains(overrideName))) return true;
// otherwise use the standard validation results
return errors == SslPolicyErrors.None;
}
}
Astuce: Vous pouvez également utiliser cette méthode pour suivre les certificats qui vont bientôt expirer. Cela peut économiser votre bacon si vous découvrez un certificat sur le point d'expirer et que vous pouvez le réparer à temps. Bon également pour les sociétés tierces - pour nous, il s’agit de DHL/FedEx. DHL vient de laisser expirer un certificat qui nous fait foutre en l'air 3 jours avant Thanksgiving. Heureusement, je suis là pour le réparer ... cette fois!
private static DateTime? _nextCertWarning;
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
if (error == SslPolicyErrors.None)
{
var cert2 = cert as X509Certificate2;
if (cert2 != null)
{
// If cert expires within 2 days send an alert every 2 hours
if (cert2.NotAfter.AddDays(-2) < DateTime.Now)
{
if (_nextCertWarning == null || _nextCertWarning < DateTime.Now)
{
_nextCertWarning = DateTime.Now.AddHours(2);
ProwlUtil.StepReached("CERT EXPIRING WITHIN 2 DAYS " + cert, cert.GetCertHashString()); // this is my own function
}
}
}
return true;
}
else
{
switch (cert.GetCertHashString())
{
// Machine certs - SELF SIGNED
case "066CF9CAD814DE2097D367F22D3A7E398B87C4D6":
return true;
default:
ProwlUtil.StepReached("UNTRUSTED CERT " + cert, cert.GetCertHashString());
return false;
}
}
}
Plusieurs réponses ci-dessus fonctionnent. Je voulais une approche qui ne nécessite pas de modifications de code ni de sécurité. J'ai donc créé une liste blanche. La liste blanche peut être gérée dans n'importe quel magasin de données. J'ai utilisé le fichier de configuration car c'est une très petite liste.
Mon code est ci-dessous.
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) => {
return error == System.Net.Security.SslPolicyErrors.None || certificateWhitelist.Contains(cert.GetCertHashString());
};
En ajoutant aux réponses de Sani et de blak3r, j'ai ajouté ce qui suit au code de démarrage de mon application, mais en VB
'** Overriding the certificate validation check.
Net.ServicePointManager.ServerCertificateValidationCallback = Function(sender, certificate, chain, sslPolicyErrors) True
Semble faire l'affaire.