Question

Je suis en train de se connecter à une API qui utilise un certificat SSL auto-signé. Je le fais en utilisant des objets HttpWebRequest et HttpWebResponse de .NET. Et je reçois une exception:

  

La connexion sous-jacente a été fermée:. N'a pas pu établir une relation de confiance pour le canal sécurisé SSL / TLS

Je comprends ce que cela signifie. Et je comprends pourquoi .NET sent qu'il devrait me prévenir et fermer la connexion. Mais dans ce cas, je voudrais juste vous connecter à l'API de toute façon, l'homme-in-the-middle attaques sont damnés.

Alors, comment dois-je procéder ajouter une exception pour ce certificat auto-signé? Ou est l'approche de dire HttpWebRequest / Réponse de ne pas valider le certificat du tout? Comment puis-je faire?

Était-ce utile?

La solution

@Domster: cela fonctionne, mais vous pouvez appliquer un peu de sécurité en vérifiant si le hachage de certificat correspond à ce que vous attendez. Ainsi, une version élargie ressemble un peu à ceci (basé sur un code en direct, nous utilisons):

static readonly byte[] apiCertHash = { 0xZZ, 0xYY, ....};

/// <summary>
/// Somewhere in your application's startup/init sequence...
/// </summary>
void InitPhase()
{
    // Override automatic validation of SSL server certificates.
    ServicePointManager.ServerCertificateValidationCallback =
           ValidateServerCertficate;
}

/// <summary>
/// Validates the SSL server certificate.
/// </summary>
/// <param name="sender">An object that contains state information for this
/// validation.</param>
/// <param name="cert">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the
/// remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote
/// certificate.</param>
/// <returns>Returns a boolean value that determines whether the specified
/// certificate is accepted for authentication; true to accept or false to
/// reject.</returns>
private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        // Good certificate.
        return true;
    }

    log.DebugFormat("SSL certificate 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;
}

Autres conseils

fin de compte, si vous voulez juste désactiver complètement la validation du certificat, vous pouvez modifier le ServerCertificateValidationCallback sur le ServicePointManager, comme suit:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

validera tous les certificats (y compris les invalides, a expiré ou les auto-signés).

Notez que dans 4,5 .NET, vous pouvez passer outre la validation SSL par HttpWebRequest lui-même (et non par l'intermédiaire délégué mondial qui affecte toutes les demandes):

http://msdn.microsoft.com/ fr-fr / bibliothèque / system.net.httpwebrequest.servercertificatevalidationcallback.aspx

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.ServerCertificateValidationCallback = delegate { return true; };

Ajoutez l'auto-signé cert aux autorités racine de confiance de l'ordinateur local de certification

Vous pouvez importer le cert en exécutant la console MMC en tant qu'administrateur.

Comment: Afficher les certificats avec le composant logiciel enfichable MMC

La portée du rappel de validation utilisé dans réponse de domster peut se limiter à une demande spécifique en utilisant le paramètre de l'expéditeur sur la délégué ServerCertificateValidationCallback. La classe simple domaine suivant utilise cette technique pour câbler temporairement un rappel de validation qui exécute uniquement pour un objet de requête donné.

public class ServerCertificateValidationScope : IDisposable
{
    private readonly RemoteCertificateValidationCallback _callback;

    public ServerCertificateValidationScope(object request,
        RemoteCertificateValidationCallback callback)
    {
        var previous = ServicePointManager.ServerCertificateValidationCallback;
        _callback = (sender, certificate, chain, errors) =>
            {
                if (sender == request)
                {
                    return callback(sender, certificate, chain, errors);
                }
                if (previous != null)
                {
                    return previous(sender, certificate, chain, errors);
                }
                return errors == SslPolicyErrors.None;
            };
        ServicePointManager.ServerCertificateValidationCallback += _callback;
    }

    public void Dispose()
    {
        ServicePointManager.ServerCertificateValidationCallback -= _callback;
    }
}

La classe ci-dessus peut être utilisé pour ignorer toutes les erreurs de certificat pour une demande spécifique comme suit:

var request = WebRequest.Create(uri);
using (new ServerCertificateValidationScope(request, delegate { return true; }))
{
    request.GetResponse();
}

Pour ajouter une aide possible à quelqu'un d'autre ... Si vous le souhaitez inviter l'utilisateur à installer le certificat auto-signé, vous pouvez utiliser ce code (modifié ci-dessus).

Ne nécessite pas de droits d'administrateur, installe aux utilisateurs locaux profils confiance:

    private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            // Good certificate.
            return true;
        }

        Common.Helpers.Logger.Log.Error(string.Format("SSL certificate 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(string.Format("SSL certificate add Error: {0}", ex.Message));
        }

        return false;
    }

Cela semble bien fonctionner pour notre application, et si l'utilisateur appuie pas, la communication ne fonctionnera pas.

Mise à jour: 11/12/2015 - Changement StoreName.Root à StoreName.My - Mon installera dans le magasin d'utilisateurs locaux, au lieu de Racine. Racine sur certains systèmes ne fonctionnera pas, même si vous « Exécuter en tant qu'administrateur »

Il suffit de construire sur la réponse de devstuff pour inclure sujet et émetteur ... commentaires de bienvenue ...

public class SelfSignedCertificateValidator
{
    private class CertificateAttributes
    {
        public string Subject { get; private set; }
        public string Issuer { get; private set; }
        public string Thumbprint { get; private set; }

        public CertificateAttributes(string subject, string issuer, string thumbprint)
        {
            Subject = subject;
            Issuer = issuer;                
            Thumbprint = thumbprint.Trim(
                new char[] { '\u200e', '\u200f' } // strip any lrt and rlt markers from copy/paste
                ); 
        }

        public bool IsMatch(X509Certificate cert)
        {
            bool subjectMatches = Subject.Replace(" ", "").Equals(cert.Subject.Replace(" ", ""), StringComparison.InvariantCulture);
            bool issuerMatches = Issuer.Replace(" ", "").Equals(cert.Issuer.Replace(" ", ""), StringComparison.InvariantCulture);
            bool thumbprintMatches = Thumbprint == String.Join(" ", cert.GetCertHash().Select(h => h.ToString("x2")));
            return subjectMatches && issuerMatches && thumbprintMatches; 
        }
    }

    private readonly List<CertificateAttributes> __knownSelfSignedCertificates = new List<CertificateAttributes> {
        new CertificateAttributes(  // 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 certificates.  
            ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertficate;

            __createdSingleton = true;
        }
    }

    /// <summary>
    /// Validates the SSL server certificate.
    /// </summary>
    /// <param name="sender">An object that contains state information for this
    /// validation.</param>
    /// <param name="cert">The certificate used to authenticate the remote party.</param>
    /// <param name="chain">The chain of certificate authorities associated with the
    /// remote certificate.</param>
    /// <param name="sslPolicyErrors">One or more errors associated with the remote
    /// certificate.</param>
    /// <returns>Returns a boolean value that determines whether the specified
    /// certificate is accepted for authentication; true to accept or false to
    /// reject.</returns>
    private bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;   // Good certificate.

        Dbg.WriteLine("SSL certificate error: {0}", sslPolicyErrors);
        return __knownSelfSignedCertificates.Any(c => c.IsMatch(cert));            
    }
}

Une chose à garder à l'esprit est que d'avoir la ServicePointManager.ServerCertificateValidationCallback ne semble pas signifier que la vérification des LCR et validation servername ne sont pas faites, il fournit seulement un moyen de passer outre leur résultat. Donc, votre service pourrait encore prendre un certain temps pour obtenir une liste de révocation, vous ne savez plus tard qu'il n'a pas certains contrôles.

Je courais dans le même problème que l'OP où la demande Web jetterait cette exception exacte. Je le certificat a été installé, je pensais que avais tout est configuré correctement, je pouvais le trouver dans le magasin de la machine très bien et le joindre à la demande web, et j'avais désactivé la vérification des certificats sur le contexte de la demande.

Il est avéré que je courais sous mon compte d'utilisateur, et que le certificat a été installé au magasin de la machine. Cela a provoqué la demande Web pour lancer cette exception. Pour résoudre le problème que je devais être soit en cours d'exécution en tant qu'administrateur ou installer le certificat au magasin de l'utilisateur et le lire à partir de là.

Il semblerait que C # est en mesure de trouver le certificat dans le magasin de la machine, même si elle ne peut pas être utilisé avec une requête Web, et que cela se traduit par une fois que la demande Web est émise étant jeté l'exception de l'OP.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top