67

私はiPhoneアプリを開発しています。開発中に、自己署名 SSL 証明書を使用しているサーバーに接続する必要があります。- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandlerこれを可能にする例外コードを書く機会があると確信しています。ただし、これを行う方法を教えてくれるリソースが見つかりません。ログに次のエラーが表示されます。

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

これに加えてNSLog(@"error = %@", error);、上記のデリゲート メソッド内から取得すると、次のようになります。

エラー Domain=NSURLErrorDomain Code=-1202 「このサーバーの証明書は無効です。「api.mydevelopmenturl.com」になりすましたサーバーに接続している可能性があり、機密情報が危険にさらされる可能性があります。」UserInfo=0x10cbdbcf0 {NSUnderlyingError=0x112ec9730 "このサーバーの証明書は無効です。「api.mydevelopmenturl.com」になりすましたサーバーに接続している可能性があり、機密情報が危険にさらされる可能性があります。", NSErrorFailingURLStringKey= https: //api.mydevelopmenturl.com/posts、NSErrorFailingURLKey= https://api.mydevelopmenturl.com/posts、NSLocalizedRecoverySuggestion=サーバーに接続しますか?、NSURLErrorFailingURLPeerTrustErrorKey=、NSLocalizedDescription=このサーバーの証明書は無効です。「api.mydevelopmenturl.com」になりすましたサーバーに接続している可能性があり、機密情報が危険にさらされる可能性があります。}

この問題を解決する方法についてのアイデアはありますか? 概念的なドキュメントを読んでも理解できないので、コードを投稿してください。これは私を超えた例です: https://developer.apple.com/library/content/technotes/tn2232/_index.html

4

11 に答える 11

7

スイフト3.0/4の場合

If you would just like to allow any kind of self-signed certificates, you could use the following approach, to implement an URLSessionDelegate. Apple provides additional information of how to use the URLSessionDelegate for all kinds of authentication methods: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html

At first implement the delegate method and assign an according delegate:

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

Now implement the delegate's method 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)
    }
}

それでも、提供されたドメインの自己署名証明書の受け入れを非常に具体的なものに適合させることができます。この証明書をビルド ターゲット バンドルに追加したことを確認してください。ここでは「cert.cer」と名付けました

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)
}
于 2017-09-13T11:29:31.187 に答える
1

これが私のために働いた解決策です。両方のメッセージを含む接続のデリゲートを介して接続を受け入れる必要があります。

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

これを行うと、証明書の信頼性をチェックしていないため、HTTPS 接続の SSL 暗号化のみが興味深いことに注意してください。ただし、署名機関はここで考慮されていないため、セキュリティが低下する可能性があります。

于 2014-11-09T02:59:37.910 に答える
0

アクセスするサービスに対して URL が正確であることを (視覚的に) 確認する証明書を受け入れる機会をユーザーに提供するのがおそらくより良い方法です。たとえば、ホストが何らかのアプリ設定に入った場合、ユーザーのエントリでテストし、ユーザーがその場で決定できるようにします。

この「ユーザー確認」戦術が Safari で使用されていることを考えると、Apple によって容認されているため、他のアプリでも論理的に採用されることは理にかなっています。

NSErrorRecoveryAttempting を掘り下げることを提案します (自分では何もしていません) http://apple.co/22Au1GR

ホストを確認してから、ここに記載されている個別の URL 除外ルートを使用します。実装によっては、後で参照できるようにホストを除外として保存することも理にかなっている場合があります。

これは、Apple が本来 Cocoa に実装していたもののように思えますが、今のところ「簡単なボタン」は見つかりませんでした。誰もがデリゲート メソッドと NSErrorRecoveryAttempting プロトコルを実装する必要があるのではなく、NSURL または NSURLSession の何かに "kLetUserDecide" フラグがあればよかったでしょう。

于 2015-12-31T03:42:52.173 に答える