web-dev-qa-db-fra.com

Erreur Swift SSL avec certificat auto-signé

Ce code tente sans succès d'accéder à une URL SSL qui fonctionne dans un navigateur: 

let path = "https://localhost:8443/greeting"
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
let session = NSURLSession.sharedSession()

let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
    let json:JSON = JSON(data: data!)
    if let c = json["content"].string {
        print(c)
    }
})
task.resume()

Échoue avec l'erreur:

Facultatif (Domaine d'erreur = Code NSURLErrorDomain = -1200 "Une erreur SSL s'est produite et une connexion sécurisée au serveur n'a pas été établie." UserInfo = {NSURLErrorFailingURLPeerTrustErrorKey =,

Que faut-il pour permettre à l'application d'accepter ce cert?

Le certificat en question est auto-signé. Lisez quelques solutions sur SO sans succès. 

Exécuter Xcode 7.2 

5
Marcus Leon

@Ashish Kakkad était sur place. Cela marche:

class Blah: NSURLSessionDelegate {

func rest() {
    let path = "https://localhost:8443/greeting"
    let request = NSMutableURLRequest(URL: NSURL(string: path)!)
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
        let json:JSON = JSON(data: data!)
        if let c = json["content"].string {
            print(c)
        }
    })
    task.resume()
}

func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}

Avec ceci dans le fichier Info.plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>localhost</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>
8
Marcus Leon

La solution de Sai Reddy vous permet d'accepter votre certificat auto-signé s'il contient une chaîne complète, mais il en accepte également d'autres.

La solution de Marcus Leon est un remplacement complet - ignorant fondamentalement tous les certificats.

J'aime mieux celui-ci.

Swift 4.1, iOS 11.4.1

Tout d'abord, dans votre Info.plist:

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

Deuxièmement, partout où vous utilisez votre NSURLSession, au lieu de configurer avec URLSession.shared, utilisez quelque chose comme ceci:

session = URLSession(configuration: .default, delegate: APIURLSessionTaskDelegate(isSSLPinningEnabled: isSSLPinningEnabled), delegateQueue: nil)

Ajoutez ensuite cette classe pour gérer l'épinglage:

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

        print("*** received SESSION challenge...\(challenge)")
        let trust = challenge.protectionSpace.serverTrust!
        let credential = URLCredential(trust: trust)

        guard isSSLPinningEnabled else {
            print("*** SSL Pinning Disabled -- Using default handling.")
            completionHandler(.useCredential, credential)
            return
        }

        let myCertName = "my_certificate_to_pin"
        var remoteCertMatchesPinnedCert = false
        if let myCertPath = Bundle.main.path(forResource: myCertName, ofType: "der") {
            if let pinnedCertData = NSData(contentsOfFile: myCertPath) {
                // Compare certificate data
                let remoteCertData: NSData = SecCertificateCopyData(SecTrustGetCertificateAtIndex(trust, 0)!)
                if remoteCertData.isEqual(to: pinnedCertData as Data) {
                    print("*** CERTIFICATE DATA MATCHES")
                    remoteCertMatchesPinnedCert = true
                }
                else {
                    print("*** MISMATCH IN CERT DATA.... :(")
                }

            } else {
                print("*** Couldn't read pinning certificate data")
            }
        } else {
            print("*** Couldn't load pinning certificate!")
        }

        if remoteCertMatchesPinnedCert {
            print("*** TRUSTING CERTIFICATE")
            completionHandler(.useCredential, credential)
        } else {
            print("NOT TRUSTING CERTIFICATE")
            completionHandler(.rejectProtectionSpace, nil)
        }
    }
}

Cette classe vérifie si vous avez activé l'épinglage de certificat. Si vous le faites, il ignore complètement la validation du certificat et effectue une comparaison exacte avec le certificat que nous incluons dans l'application. De cette façon, il seulement accepte votre certificat auto-signé, et rien d’autre.

Cette solution nécessite que vous placiez un fichier " my_certificate_to_pin.der " dans votre projet, dans votre dossier Ressources. Si vous ne possédez pas déjà de dossier Ressources, ajoutez-en un.

Ce certificat doit être au format DER.

Pour créer un certificat auto-signé pour votre serveur, procédez comme suit:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mycert.key -out mycert.cer

Cela génère deux fichiers - un fichier mycert.key clé privée, et un mycert.cer - le certificat lui-même. Ce sont tous deux au format X509. Pour iOS, vous aurez besoin du cert au format DER, procédez comme suit:

openssl x509 -outform der -in mycert.cer -out my_certificate_to_pin.der

Cela génère le fichier dont vous avez besoin sur iOS.

5
drewster

vous pouvez utiliser vos propres certificats au lieu de mes certificats (fullchain.pem)

    class AccessingServer: NSObject,URLSessionDelegate {

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

        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            // First load our extra root-CAs to be trusted from the app bundle.
            let trust = challenge.protectionSpace.serverTrust

            let rootCa = "fullchain"
            if let rootCaPath = Bundle.main.path(forResource: rootCa, ofType: "pem") {
                if let rootCaData = NSData(contentsOfFile: rootCaPath) {

                    let rootCert = SecCertificateCreateWithData(nil, rootCaData)

                    // let certArrayRef = CFArrayCreate(nil, UnsafeMutablePointer<UnsafePointer<Any>>([rootCert]), 1, nil)

                    SecTrustSetAnchorCertificates(trust!, rootCert as! CFArray)

                    SecTrustSetAnchorCertificatesOnly(trust!, false)
                }
            }

            var trustResult: SecTrustResultType = SecTrustResultType.invalid
            SecTrustEvaluate(trust!, &trustResult)

            if (trustResult == SecTrustResultType.unspecified ||
                trustResult == SecTrustResultType.proceed) {
                // Trust certificate.

                let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
                challenge.sender?.use(credential, for: challenge)

            } else {
                NSLog("Invalid server certificate.")
                challenge.sender?.cancel(challenge)
            }
        } else {
            NSLog("Got unexpected authentication method \(challenge.protectionSpace.authenticationMethod)");
            challenge.sender?.cancel(challenge)
        }
    }

   }
2
Sai kumar Reddy