J’essaie de me connecter à une API utilisant un certificate SSL auto-signé. Je le fais en utilisant les objects HttpWebRequest et HttpWebResponse de .NET. Et je reçois une exception qui:
La connexion sous-jacente a été fermée: Impossible d’établir une relation de confiance pour le canal sécurisé SSL / TLS.
Je comprends ce que cela signifie. Et je comprends pourquoi .NET pense qu’il doit me prévenir et fermer la connexion. Mais dans ce cas, je voudrais tout simplement me connecter à l’API, les attaques de type intermédiaire doivent être condamnées.
Alors, comment puis-je append une exception pour ce certificate auto-signé? Ou l’approche consistant à indiquer à HttpWebRequest / Response de ne pas valider le certificate? Comment pourrais-je faire ça?
@Domster: ça marche, mais vous voudrez peut-être renforcer la sécurité en vérifiant si le hachage du certificate correspond à vos attentes. Donc, une version étendue ressemble un peu à ceci (basé sur du code live que nous utilisons):
static readonly byte[] apiCertHash = { 0xZZ, 0xYY, ....}; /// /// Somewhere in your application's startup/init sequence... /// void InitPhase() { // Override automatic validation of SSL server certificatees. ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertficate; } /// /// Validates the SSL server certificatee. /// /// An object that contains state information for this /// validation. /// The certificatee used to authenticate the remote party. /// The chain of certificatee authorities associated with the /// remote certificatee. /// One or more errors associated with the remote /// certificatee. /// Returns a boolean value that determines whether the specified /// certificatee is accepted for authentication; true to accept or false to /// reject. private static bool ValidateServerCertficate( object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors) { if (sslPolicyErrors == SslPolicyErrors.None) { // Good certificatee. return true; } log.DebugFormat("SSL certificatee error: {0}", sslPolicyErrors); bool certMatch = false; // Assume failure byte[] certHash = cert.GetCertHash(); if (certHash.Length == apiCertHash.Length) { certMatch = true; // Now assume success. for (int idx = 0; idx < certHash.Length; idx++) { if (certHash[idx] != apiCertHash[idx]) { certMatch = false; // No match break; } } } // Return true => allow unauthenticated server, // false => disallow unauthenticated server. return certMatch; }
Si vous souhaitez simplement désactiver la validation des certificates, vous pouvez modifier ServerCertificateValidationCallback sur le ServicePointManager, comme ceci:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Cela validera tous les certificates (y compris les certificates non valides, expirés ou auto-signés).
Ajouter le certificate auto-signé aux autorités de certificateion racine de confiance de l’ordinateur local
Vous pouvez importer le cert en exécutant la console MMC en tant qu’administrateur.
Comment: afficher les certificates avec le composant logiciel enfichable MMC
Notez que dans .NET 4.5, vous pouvez remplacer la validation SSL par HttpWebRequest lui-même (et non via un délégué global qui affecte toutes les demandes):
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); request.ServerCertificateValidationCallback = delegate { return true; };
L’étendue du rappel de validation utilisé dans la réponse de Domster peut être limitée à une demande spécifique à l’aide du paramètre émetteur du délégué ServerCertificateValidationCallback
. La classe de scope simple suivante utilise cette technique pour câbler temporairement un rappel de validation qui ne s’exécute que pour un object de requête donné.
public class ServerCertificateValidationScope : IDisposable { private readonly RemoteCertificateValidationCallback _callback; public ServerCertificateValidationScope(object request, RemoteCertificateValidationCallback callback) { var previous = ServicePointManager.ServerCertificateValidationCallback; _callback = (sender, certificatee, chain, errors) => { if (sender == request) { return callback(sender, certificatee, chain, errors); } if (previous != null) { return previous(sender, certificatee, chain, errors); } return errors == SslPolicyErrors.None; }; ServicePointManager.ServerCertificateValidationCallback += _callback; } public void Dispose() { ServicePointManager.ServerCertificateValidationCallback -= _callback; } }
La classe ci-dessus peut être utilisée pour ignorer toutes les erreurs de certificate pour une demande spécifique, comme suit:
var request = WebRequest.Create(uri); using (new ServerCertificateValidationScope(request, delegate { return true; })) { request.GetResponse(); }
Pour append une aide possible à quelqu’un d’autre … Si vous souhaitez qu’il invite l’utilisateur à installer le certificate auto-signé, vous pouvez utiliser ce code (modifié ci-dessus).
Ne nécessite pas de droits d’administrateur, installe aux utilisateurs locaux des profils de confiance:
private static bool ValidateServerCertficate( object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors) { if (sslPolicyErrors == SslPolicyErrors.None) { // Good certificatee. return true; } Common.Helpers.Logger.Log.Error(ssortingng.Format("SSL certificatee error: {0}", sslPolicyErrors)); try { using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadWrite); store.Add(new X509Certificate2(cert)); store.Close(); } return true; } catch (Exception ex) { Common.Helpers.Logger.Log.Error(ssortingng.Format("SSL certificatee add Error: {0}", ex.Message)); } return false; }
Cela semble bien fonctionner pour notre application, et si l’utilisateur appuie sur Non, la communication ne fonctionnera pas.
Mise à jour: 11/12/2015 – Changement StoreName.Root à StoreName.My – Mon installera dans le magasin d’utilisateurs local, au lieu de racine. Racine sur certains systèmes ne fonctionnera pas, même si vous “exécutez en tant qu’administrateur”
Il suffit de s’appuyer sur la réponse de devstuff pour inclure le sujet et l’émetteur … commentaires bienvenus …
public class SelfSignedCertificateValidator { private class CertificateAtsortingbutes { public ssortingng Subject { get; private set; } public ssortingng Issuer { get; private set; } public ssortingng Thumbprint { get; private set; } public CertificateAtsortingbutes(ssortingng subject, ssortingng issuer, ssortingng thumbprint) { Subject = subject; Issuer = issuer; Thumbprint = thumbprint.Trim( new char[] { '\u200e', '\u200f' } // ssortingp any lrt and rlt markers from copy/paste ); } public bool IsMatch(X509Certificate cert) { bool subjectMatches = Subject.Replace(" ", "").Equals(cert.Subject.Replace(" ", ""), SsortingngComparison.InvariantCulture); bool issuerMatches = Issuer.Replace(" ", "").Equals(cert.Issuer.Replace(" ", ""), SsortingngComparison.InvariantCulture); bool thumbprintMatches = Thumbprint == Ssortingng.Join(" ", cert.GetCertHash().Select(h => h.ToSsortingng("x2"))); return subjectMatches && issuerMatches && thumbprintMatches; } } private readonly List __knownSelfSignedCertificates = new List { new CertificateAtsortingbutes( // can paste values from "view cert" dialog "CN = subject.company.int", "CN = issuer.company.int", "f6 23 16 3d 5a d8 e5 1e 13 58 85 0a 34 9f d6 d3 c8 23 a8 f4") }; private static bool __createdSingleton = false; public SelfSignedCertificateValidator() { lock (this) { if (__createdSingleton) throw new Exception("Only a single instance can be instanciated."); // Hook in validation of SSL server certificatees. ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertficate; __createdSingleton = true; } } /// /// Validates the SSL server certificatee. /// /// An object that contains state information for this /// validation. /// The certificatee used to authenticate the remote party. /// The chain of certificatee authorities associated with the /// remote certificatee. /// One or more errors associated with the remote /// certificatee. /// Returns a boolean value that determines whether the specified /// certificatee is accepted for authentication; true to accept or false to /// reject. private bool ValidateServerCertficate( object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors) { if (sslPolicyErrors == SslPolicyErrors.None) return true; // Good certificatee. Dbg.WriteLine("SSL certificatee error: {0}", sslPolicyErrors); return __knownSelfSignedCertificates.Any(c => c.IsMatch(cert)); } }
Une chose à garder à l’esprit est qu’avoir le ServicePointManager.ServerCertificateValidationCallback ne semble pas signifier que la vérification de la liste de révocation de certificates et la validation du nom de serveur ne sont pas effectuées, cela ne permet que de remplacer leur résultat. Il se peut donc que votre service prenne un certain temps pour obtenir une liste de révocation de certificates, mais vous saurez ensuite que certaines vérifications ont échoué.
Je rencontrais le même problème que l’OP où la requête Web lançait cette exception exacte. Je pensais que tout était correctement configuré, que le certificate était installé, que je pouvais le localiser dans le magasin de machines et l’attacher à la demande Web. J’avais désactivé la vérification des certificates dans le contexte de la demande.
Il s’est avéré que j’étais en cours d’exécution sous mon compte d’utilisateur et que le certificate était installé sur le magasin de machines. Cela a provoqué la requête Web pour lancer cette exception. Pour résoudre le problème, je devais soit exécuter en tant qu’administrateur, soit installer le certificate dans le magasin de l’utilisateur et le lire à partir de là.
Il semblerait que C # soit capable de trouver le certificate dans le magasin de machines même s’il ne peut pas être utilisé avec une requête Web, et que cela entraîne la levée de l’exception du PO une fois la demande Web émise.