it-swarm.com.de

Verwenden eines selbstsignierten Zertifikats mit HttpWebRequest / Response von .NET

Ich versuche, eine Verbindung zu einer API herzustellen, die ein selbstsigniertes SSL-Zertifikat verwendet. Ich verwende dazu die .NET-Objekte HttpWebRequest und HttpWebResponse. Und ich bekomme eine Ausnahme:

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

Ich verstehe, was das bedeutet. Und ich verstehe warum .NET meint, es sollte mich warnen und die Verbindung schließen. Aber in diesem Fall möchte ich mich trotzdem nur mit der API verbinden, Man-in-the-Middle-Angriffe sind verdammt.

Wie füge ich eine Ausnahme für dieses selbstsignierte Zertifikat hinzu? Oder ist der Ansatz, HttpWebRequest/Response anzuweisen, das Zertifikat überhaupt nicht zu validieren? Wie würde ich das machen?

77

@Domster: Das funktioniert, aber Sie möchten möglicherweise ein bisschen Sicherheit durchsetzen, indem Sie prüfen, ob der Zertifikatshash mit dem übereinstimmt, was Sie erwarten. Eine erweiterte Version sieht also ungefähr so ​​aus (basierend auf dem von uns verwendeten Live-Code):

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

Wenn Sie die Zertifikatsüberprüfung nur ganz deaktivieren möchten, können Sie den ServerCertificateValidationCallback im ServicePointManager folgendermaßen ändern:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Dadurch werden alle Zertifikate (einschließlich ungültiger, abgelaufener oder selbstsignierter) validiert.

87

Beachten Sie, dass Sie in .NET 4.5 die SSL-Validierung per HttpWebRequest selbst überschreiben können (und nicht über einen globalen Delegaten, der alle Anforderungen betrifft):

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

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

Fügen Sie das selbstsignierte Zertifikat den vertrauenswürdigen Stammzertifizierungsstellen des lokalen Computers hinzu

Sie können das Zertifikat importieren, indem Sie MMC als Administrator ausführen.

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

42
wgthom

Der Umfang des in Domsters Antwort verwendeten Validierungsrückrufs kann mithilfe des Absenderparameters auf dem ServerCertificateValidationCallback -Delegaten auf eine bestimmte Anforderung beschränkt werden. In der folgenden einfachen Bereichsklasse wird diese Technik verwendet, um einen Validierungsrückruf vorübergehend zu verknüpfen, der nur für ein bestimmtes Anforderungsobjekt ausgeführt wird.

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 kann verwendet werden, um alle Zertifikatfehler für eine bestimmte Anforderung wie folgt zu ignorieren:

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

So fügen Sie jemandem eine mögliche Hilfe hinzu: Wenn Sie den Benutzer zur Installation des selbstsignierten Zertifikats auffordern möchten, können Sie diesen Code verwenden (von oben geändert).

Erfordert keine Administratorrechte, wird auf den vertrauenswürdigen Profilen der lokalen Benutzer installiert:

    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 Nein drückt, funktioniert die Kommunikation nicht.

pdate: 2015-12-11 - StoreName.Root in StoreName.My geändert - My wird anstelle von Root im lokalen Benutzerspeicher installiert. Root funktioniert auf einigen Systemen nicht, auch wenn Sie als Administrator ausgeführt werden.

3
TravisWhidden

Einfach auf der Antwort von devstuff aufbauen, um Betreff und Aussteller einzuschließen ... Kommentare erwünscht ...

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

Ich hatte das gleiche Problem wie beim OP, bei dem die Webanforderung genau diese Ausnahme auslösen würde. Ich hatte alles richtig eingerichtet. Ich dachte, das Zertifikat sei installiert, ich könnte es im Computerspeicher finden und an die Webanforderung anhängen, und ich hatte die Überprüfung von Zertifikaten im Anforderungskontext deaktiviert.

Es stellte sich heraus, dass ich unter meinem Benutzerkonto ausgeführt wurde und das Zertifikat im Computerspeicher installiert wurde. Dies führte dazu, dass die Webanforderung diese Ausnahme auslöste. Um das Problem zu lösen, musste ich entweder als Administrator ausgeführt werden oder das Zertifikat im Benutzerspeicher installieren und von dort aus lesen.

Es scheint, dass C # das Zertifikat im Computerspeicher finden kann, obwohl es nicht für eine Webanforderung verwendet werden kann, und dass dies dazu führt, dass die OP-Ausnahme ausgelöst wird, sobald die Webanforderung ausgestellt wird.

1
Simon Ejsing

Beachten Sie, dass der ServicePointManager.ServerCertificateValidationCallback nicht bedeutet, dass die CRL-Prüfung und die Validierung des Servernamens nicht durchgeführt werden, sondern lediglich eine Möglichkeit zum Überschreiben des Ergebnisses bietet. Es kann also noch eine Weile dauern, bis Ihr Service eine CRL abgerufen hat. Erst danach wissen Sie, dass einige Überprüfungen fehlgeschlagen sind.

1
nicki