Frage

Ich versuche, eine API zu verbinden, die ein selbst signierten SSL-Zertifikat verwendet. Ich mache das so mit .NET HttpWebRequest und HttpWebResponse Objekte. Und ich bin immer eine Ausnahme, dass:

  

Die zugrunde liegende Verbindung wurde geschlossen:. Konnte keine Vertrauensstellung für den sicheren SSL / TLS-Kanal einzurichten

Ich verstehe, was das bedeutet. Und ich verstehe Warum .NET fühlt es sollte mich warnen und die Verbindung zu schließen. Aber in diesem Fall, würde Ich mag zu einfach mal so an die API verbinden, Man-in-the-Middle-Angriffe verdammt werden.

Also, wie gehe ich über eine Ausnahme für dieses selbst signiertes Zertifikat hinzufügen? Oder ist der Ansatz zu sagen, HttpWebRequest / Response nicht das Zertifikat überhaupt zu validieren? Wie würde ich das tun?

War es hilfreich?

Lösung

@Domster: das funktioniert, aber Sie könnten ein wenig Sicherheit durch Überprüfung erzwingen möchten, wenn das Zertifikat-Hash übereinstimmt, was Sie erwarten. So eine erweiterte Version sieht ein bisschen wie diese (basierend auf einige Live-Code, den wir verwenden):

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;
}

Andere Tipps

Es stellte sich heraus, wenn Sie nur ganz Zertifikatsüberprüfung deaktivieren möchten, können Sie die ServerCertificateValidationCallback auf der Servicepoint ändern, etwa so:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Damit werden alle Zertifikate (einschließlich ungültig, abgelaufen oder selbstsignierte sind) validieren.

Beachten Sie, dass in 4,5 .NET können Sie SSL-Validierung pro HttpWebRequest selbst (und nicht über global Delegierten, die alle Anforderungen betreffen) außer Kraft setzen:

http://msdn.microsoft.com/ en-us / library / system.net.httpwebrequest.servercertificatevalidationcallback.aspx

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

Fügen Sie das selbst signiertes Zertifikat auf den lokalen Computer Vertrauenswürdige Stammzertifizierungsstellen

Sie können das Zertifikat importieren, indem Sie die MMC als Administrator ausgeführt wird.

Gewusst wie: Anzeigen von Zertifikaten mit dem MMC-Snap-in

Der Umfang der Validierung Rückrufs in Domster Antwort auf eine bestimmte Anforderung beschränkt werden kann, die Sender-Parameter auf der Verwendung von ServerCertificateValidationCallback delegieren. Die folgende einfache Rahmen-Klasse verwendet diese Technik, um vorübergehend einen Validierung Rückruf verdrahten, die für ein bestimmtes Request-Objekt führt nur.

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;
    }
}

Die obige Klasse verwendet werden kann, alle Zertifikatsfehler für eine bestimmte Anforderung zu ignorieren wie folgt:

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

als mögliche Hilfe jemand hinzuzufügen sonst ... Wenn Sie es wünschen, den Benutzer aufzufordern, die selbst signiertes Zertifikat zu installieren, können Sie diesen Code verwenden (von oben modifiziert).

Hat Administratorrechte nicht erforderlich, installiert an die lokalen Benutzer-Profile vertraut:

    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;
    }

Dies scheint für unsere Anwendung gut zu funktionieren, und wenn der Benutzer nicht drückt, wird die Kommunikation nicht.

Update: 2015.12.11 - Changed StoreName.Root zu StoreName.My - My wird in den lokalen Benutzer Speicher installieren, statt Wurzel. Root auf einigen Systemen wird nicht funktionieren, auch wenn Sie „Als Administrator ausführen“

Just auf Antwort Bau von devstuff Thema und Emittenten gehören ... Kommentare willkommen ...

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));            
    }
}

Eine Sache im Auge zu behalten ist, dass der ServicePointManager.ServerCertificateValidationCallback mit nicht zu verstehen scheint, dass die CRL-Prüfung und Servervalidierung nicht getan, es gibt nur ein Mittel, ihr Ergebnis zu überschreiben. Also Ihr Service könnte noch eine Weile dauern, eine CRL zu erhalten, werden Sie erst dann wissen, dass es einige Prüfungen fehlgeschlagen.

Ich lief in das gleiche Problem wie die OP, wo die Web-Anfrage, dass die genaue Ausnahme auslösen würde. Ich hatte alles richtig Setup dachte ich, wurde das Zertifikat installiert, ich habe es in der Maschine Speicher ausfindig machen konnte ganz gut und lege es auf die Web-Anfrage, und ich hatte die Überprüfung der Zertifikate auf dem Anforderungskontext deaktiviert.

Es stellte sich heraus, dass ich unter meinem Benutzerkonto ausgeführt wurde, und dass das Zertifikat dem Computerspeicher installiert wurde. Dies verursachte die Web-Anfrage, diese Ausnahme zu werfen. Um das Problem zu lösen hatte ich entweder als Administrator ausgeführt werden oder das Zertifikat auf den Benutzerspeicher installieren und von dort lesen.

Es scheint, dass C # die Lage ist, das Zertifikat im Computerspeicher zu finden, auch wenn es nicht mit einer Web-Anforderung verwendet werden kann, und dass dies in dem OP der Ausnahme führt einmal Anforderung ausgegeben wird die Bahn geworfen zu werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top