web-dev-qa-db-de.com

Wie akzeptiere ich ein selbstsigniertes SSL-Zertifikat mit NSURLSession von iOS 7 und seiner Delegatenfamilie für Entwicklungszwecke?

Ich entwickle eine iPhone-App. Während der Entwicklung muss ich eine Verbindung zu einem Server herstellen, der ein selbstsigniertes SSL-Zertifikat verwendet. Ich bin mir ziemlich sicher, dass - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler meine Gelegenheit ist, einen Ausnahmecode zu schreiben, um dies zu ermöglichen. Ich kann jedoch keine Ressourcen finden, die mir sagen, wie das geht. Ich kann den folgenden Fehler im Protokoll sehen:

NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)

Wenn ich aus der obigen Delegat-Methode NSLog(@"error = %@", error); komme, bekomme ich zusätzlich:

Fehler Domain = NSURLErrorDomain Code = -1202 "Das Zertifikat für Dieses Servers ist ungültig. Möglicherweise stellen Sie eine Verbindung zu einem Server her, der Ist und" api.mydevelopmenturl.com "ist. Dies könnte Ihre Vertraulich machen gefährdete Informationen. " UserInfo = 0x10cbdbcf0 {NSUnderlyingError = 0x112ec9730 "Das Zertifikat für diesen Server ist Ungültig. Möglicherweise stellen Sie eine Verbindung zu einem Server her, der vorgibt, " Api.mydevelopmenturl.com "zu sein, was Ihre vertraulichen Informationen ", NSErrorFailingURLStringKey = https://api.mydevelopmenturl.com/posts , NSErrorFailingURLKey = https://api.mydevelopmenturl.com/posts , NSLocalizedRecoverySuggestion = Möchten Sie eine Verbindung zu .__ herstellen. Server trotzdem ?, NSURLErrorFailingURLPeerTrustErrorKey =, NSLocalizedDescription = Das Zertifikat für diesen Server ist ungültig . Möglicherweise stellen Sie eine Verbindung zu einem Server her, der sich als .__ ausgibt. "Api.mydevelopmenturl.com", das Ihre vertraulichen Daten verteilen könnte gefährdete Informationen.}

Ideen zur Lösung dieses Problems? Bitte geben Sie den Code ein, da ich die konzeptionellen Dokumente gelesen habe und sie nicht verstehe. Hier ist ein Beispiel, das mir nicht gerecht wird: https://developer.Apple.com/library/content/technotes/tn2232/_index.html

54
John Erck

Das funktioniert für mich:

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:Nil];
...
...
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
    if([challenge.protectionSpace.Host isEqualToString:@"mydomain.com"]){
      NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
      completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }
  }
}
105
friherd

Apple hat eine Technical Note 2232 , die recht informativ ist und ausführlich erläutert HTTPS-Serververtrauensbewertung .

In diesem Fall lautet der Fehler -1202 in der Domäne NSURLErrorDomainNSURLErrorServerCertificateUntrusted. Dies bedeutet, dass die Bewertung der Serververtrauensstellung fehlgeschlagen ist. Möglicherweise erhalten Sie auch eine Reihe anderer Fehler. Anhang A: Fehler bei der Bewertung der allgemeinen Vertrauenswürdigkeit von Servern listet die häufigsten Fehler auf.

Aus der technischen Anmerkung:

In den meisten Fällen ist der beste Weg, eine Serververtrauensbewertung aufzulösen Fehler ist der Server zu beheben. Dies hat zwei Vorteile: Es bietet die beste Sicherheit und reduziert die Menge an Code, die Sie schreiben müssen. Das Der Rest dieser Technote beschreibt, wie Sie Server-Vertrauen diagnostizieren können Auswertungsfehler und, wenn es nicht möglich ist, den Server zu reparieren, wie Sie können die Serververtrauensbewertung anpassen, um die Verbindung mit .__ zuzulassen. fortfahren, ohne die Sicherheit des Benutzers vollständig zu beeinträchtigen.

Das spezielle Bit, das für diese Frage von Bedeutung ist, ist der Abschnitt zur NSURLSession Server Trust-Bewertung :

Mit NSURLSession können Sie die HTTPS-Serververtrauensbewertung mit .__ anpassen. Implementierung des -URLSession:didReceiveChallenge:completionHandler: Methode delegieren. Suchen Sie nach .__, um die HTTPS-Serververtrauensbewertung anzupassen. eine Herausforderung, deren Schutzbereich über die Authentifizierungsmethode .__ verfügt. NSURLAuthenticationMethodServerTrust. Löse für diese Herausforderungen sie wie unten beschrieben. Für andere Herausforderungen die nicht Wenn Sie sich darum kümmern, rufen Sie den Completion-Handler-Block mit der .__ auf. NSURLSessionAuthChallengePerformDefaultHandling disposition und eine NULL Bescheinigung.

Beim Umgang mit NSURLAuthenticationMethodServerTrust Authentifizierungsherausforderung, können Sie das Vertrauensobjekt von der .__ erhalten. Der Schutzraum der Herausforderung durch Aufrufen der -serverTrust-Methode. Nach dem Verwenden des Vertrauensobjekts, um Ihre eigene benutzerdefinierte HTTPS-Server-Vertrauensstellung zu erfüllen Bei der Evaluierung müssen Sie die Herausforderung auf zwei Arten lösen:

Wenn Sie die Verbindung ablehnen möchten, rufen Sie den Completion-Handler-Block mit .__ auf. die NSURLSessionAuthChallengeCancelAuthenticationChallenge Disposition und ein NULL-Ausweis.

Wenn Sie die Verbindung zulassen möchten, erstellen Sie einen Berechtigungsnachweis aus Ihrer Vertrauensobjekt (mit +[NSURLCredential credentialForTrust:]) und Rufen Sie den Completion-Handler-Block mit diesem Berechtigungsnachweis und dem .__ auf. NSURLSessionAuthChallengeUseCredential Disposition.

Die Folge davon ist, dass Sie, wenn Sie die folgende Delegatmethode implementieren, die Serververtrauensstellung für einen bestimmten Server überschreiben können:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    if([challenge.protectionSpace.authenticationMethod
                           isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        if([challenge.protectionSpace.Host
                           isEqualToString:@"domaintoverride.com"])
        {
            NSURLCredential *credential = 
                          [NSURLCredential credentialForTrust:
                                          challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        }
        else
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}

Beachten Sie, dass Sie mit sowohl als auch mit dem Fall des Hosts übereinstimmen müssen, den Sie mit und in allen anderen Fällen überschreiben möchten. Wenn Sie den Teil "alle anderen Fälle" nicht behandeln, ist das Verhaltensergebnis undefiniert. 

24
memmons

Finden Sie online eine vertrauenswürdige SSL-Zertifizierungsstelle, die eine kostenlose 90-Tage-Testversion für neue Zertifikate anbietet. Installieren Sie das Zertifikat auf Ihrem Server. Sie haben jetzt 90 Tage Zeit, um Ihre App so weit zu entwickeln, dass Sie entscheiden können, ob es sich lohnt, Geld zu zahlen, um das Zertifikat zu "erneuern". Dies ist die beste Antwort für mich, da meine Entscheidung, das selbstsignierte Zertifikat zu verwenden, finanziell motiviert war und ich 90 Tage Zeit habe, um meine App so weit zu entwickeln, dass ich entscheiden kann, ob es sich lohnt, Geld für ein SSL-Zertifikat auszugeben oder nicht . Dieser Ansatz vermeidet, dass Sie sich mit den Auswirkungen auf die Sicherheit auseinandersetzen müssen, wenn eine Codebase ausgeführt wird, die für das Akzeptieren selbstsignierter Zertifikate optimiert ist. Süss! Yay für das Bootstrapping!

11
John Erck

Tun Sie sich selbst einen großen Gefallen und tun Sie es nicht.

Lesen Sie zunächst die Zeitung Der gefährlichste Code der Welt: Validierung von SSL-Zertifikaten in Nicht-Browser-Software , insbesondere Abschnitt 10 Msgstr "Zertifikatsüberprüfung unterbrechen oder deaktivieren". Es wird speziell ein Blog zum Thema Kakao aufgerufen, in dem genau beschrieben wird, wie Sie Ihre Fragen beantworten.

Aber nicht. Das Deaktivieren der SSL-Zertifikatsprüfung entspricht dem Einführen einer tickenden Zeitbombe in Ihre App. Irgendwann, eines Tages, wird es versehentlich aktiviert bleiben und ein Build wird in die Wildnis geraten. Und an diesem Tag sind Ihre Benutzer einem ernsthaften Risiko ausgesetzt.

Stattdessen sollten Sie ein Zertifikat verwenden, das mit einem Zwischenzertifikat signiert ist, das Sie auf diesem bestimmten Gerät installieren und dem Sie vertrauen können, damit die SSL-Validierung erfolgreich ist, ohne ein anderes Gerät als Ihr eigenes zu gefährden (und nur dann, vorübergehend).

10
Shaggy Frog

Entspricht der Lösung von Friherd, aber in Swift:

func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust{
        let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential);
    }
}
6
eric

Für Swift 3.0/4

Wenn Sie nur beliebige selbstsignierte Zertifikate zulassen möchten, können Sie zum Implementieren einer URLSessionDelegate den folgenden Ansatz verwenden: __ Apple bietet zusätzliche Informationen zur Verwendung der URLSessionDelegate für alle Arten von Authentifizierungsmethoden: https : //developer.Apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html

Implementieren Sie zunächst die Delegatenmethode und weisen Sie einen entsprechenden Delegierten zu:

let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
let task = urlSession.dataTask(with: urlRequest).resume()

Implementieren Sie nun die Methode des Delegaten https://developer.Apple.com/documentation/foundation/nsurlsessiondelegate/1409308-urlsession?language=objc

func urlSession(_ session: URLSession, 
     didReceive challenge: URLAuthenticationChallenge, 
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard challenge.previousFailureCount == 0 else {
        challenge.sender?.cancel(challenge)
        // Inform the user that the user name and password are incorrect
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }

    // Within your authentication handler delegate method, you should check to see if the challenge protection space has an authentication type of NSURLAuthenticationMethodServerTrust
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
       // and if so, obtain the serverTrust information from that protection space.
       && challenge.protectionSpace.serverTrust != nil
       && challenge.protectionSpace.Host == "yourdomain.com" {
        let proposedCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
        completionHandler(URLSession.AuthChallengeDisposition.useCredential, proposedCredential)
    }
}

Sie können jedoch die Akzeptanz eines selbstsignierten Zertifikats für Ihre bereitgestellte Domain anpassen, um sie an eine bestimmte Domäne anzupassen. Stellen Sie sicher, dass Sie dieses Zertifikat vor dem Build-Ziel-Bundle hinzugefügt haben. Ich habe es hier "cert.cer" genannt

func urlSession(_ session: URLSession, 
     didReceive challenge: URLAuthenticationChallenge, 
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard challenge.previousFailureCount == 0 else {
        challenge.sender?.cancel(challenge)
        // Inform the user that the user name and password are incorrect
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }

    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
       && challenge.protectionSpace.serverTrust != nil
       && challenge.protectionSpace.Host == "yourdomain.com" {

        if let trust = challenge.protectionSpace.serverTrust,
           let pem = Bundle.main.url(forResource:"cert", withExtension: "cer"),
           let data = NSData(contentsOf: pem),
           let cert = SecCertificateCreateWithData(nil, data) {
            let certs = [cert]
            SecTrustSetAnchorCertificates(trust, certs as CFArray)
            var result=SecTrustResultType.invalid
            if SecTrustEvaluate(trust,&result)==errSecSuccess {
              if result==SecTrustResultType.proceed || result==SecTrustResultType.unspecified {
                let proposedCredential = URLCredential(trust: trust)
                completionHandler(.useCredential,proposedCredential)
                return
              }
            }

        }
    }
    completionHandler(.performDefaultHandling, nil)
}
3
Lepidopteron

sie müssen lediglich .cer zu SecTrust hinzufügen und ATS weiterleiten

class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if let trust = challenge.protectionSpace.serverTrust,
               let pem = Bundle.main.path(forResource: "https", ofType: "cer"),
               let data = NSData(contentsOfFile: pem),
               let cert = SecCertificateCreateWithData(nil, data) {
                let certs = [cert]
                SecTrustSetAnchorCertificates(trust, certs as CFArray)

                completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: trust))
                return
            }
        }

        // Pinning failed
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }
}
1
yycking

Hier ist die Lösung, die für mich funktioniert hat ..__ Sie müssen die Verbindung über den Delegaten der Verbindung akzeptieren, der beide Nachrichten enthält:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

Beachten Sie, dass Sie dabei nicht die Vertrauenswürdigkeit des Zertifikats überprüfen. Daher ist nur die SSL-Verschlüsselung der HTTPS-Verbindung interessant. Die Unterschriftsberechtigung berücksichtigt hierbei jedoch nicht, wodurch die Sicherheit beeinträchtigt wird.

0
Maxime T

Dies funktioniert gut für mich selbst vorbei signiert:

Delegate : NSURLSessionDelegate

- (void)URLSession:(NSURLSession *)session **task**:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
0

xcode 9 aktualisieren

    var result:(message:String, data:Data?) = (message: "Fail", data: nil)
    var request = URLRequest(url: url)

    let sessionDelegate = SessionDelegate()
    let session = URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: nil)
    let task = session.dataTask(with: request){(data, response, error) in


    }
    task.resume()

die delegierte Aufgabe

    class SessionDelegate:NSObject, URLSessionDelegate
    {

        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust)
            {
                print(challenge.protectionSpace.Host) 
                if(challenge.protectionSpace.Host == "111.11.11.11")
                {
                    let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
                   completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential)
                }
            }

        }
    }
0
luhuiya

Vielleicht bietet es sich an, dem Benutzer die Möglichkeit zu geben, das Zertifikat anzunehmen, das (visuell) bestätigt, dass die URL für den Dienst, auf den zugegriffen wird, korrekt ist. Wenn der Host beispielsweise in einer App-Einstellung eingetragen ist, testen Sie den Eintrag des Benutzers und lassen Sie den Benutzer direkt dort entscheiden. 

Wenn man bedenkt, dass diese "Benutzerbestätigung" -Taktik von Safari verwendet wird, also von Apple geduldet wird, wäre es sinnvoll, dass sie für andere Apps logisch verwendet wird. 

Schlage vor, in NSErrorRecoveryAttempting zu graben (mache ich selbst nicht) http://Apple.co/22Au1GR

Lassen Sie den Host bestätigen, und nehmen Sie die hier angegebene URL-Ausschlussroute. Je nach Implementierung kann es auch sinnvoll sein, den Host als Ausschluss für die spätere Bezugnahme zu speichern. 

Dies scheint etwas zu sein, das Apple von Natur aus in Cocoa implementiert hätte, aber bis jetzt habe ich noch keinen "einfachen Button" gefunden. Hätte ein Flag "kLetUserDecide" für etwas in NSURL oder NSURLSession gewünscht, anstatt dass jeder die Delegat-Methode sowie das Protokoll NSErrorRecoveryAttempting implementieren musste.

0
William Cerniuk