【问题标题】:Server Authentication in Swift 2.0 & XCode 7 brokenSwift 2.0 和 XCode 7 中的服务器身份验证被破坏
【发布时间】:2015-06-18 09:13:47
【问题描述】:

我刚刚将我的代码更新到 Swift 2.0 以使用 Xcode 7。我的应用程序执行 NSURLAuthenticationMethodServerTrust 和 NSURLAuthenticationMethodClientCertificate 身份验证。

问题是NSURLAuthenticationMethodServerTrust 身份验证在我的模拟器上停止工作 - 但在我的 iOS 8.3 测试设备上仍然有效。除了我不是 Swift 2.0 的旧项目外,它还在工作。

错误:NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

从 NSURLSession 检索到的错误:

Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo=0x7fcf75053070 {NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x7fcf73700d00>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorCodeKey=-9802, NSUnderlyingError=0x7fcf735284b0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1200.)", NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://mywebapi/dosomething, NSErrorFailingURLStringKey=https://mywebapi/dosomething, _kCFStreamErrorDomainKey=3} [GetOneTimeTokenController.swift:76]

我仍然以 iOS 8.0 为目标进行部署。

这是我处理身份验证挑战的方式(使用自签名证书):

if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {

        let urlCredential:NSURLCredential = NSURLCredential(
            identity: identityAndTrust.identityRef,
            certificates: identityAndTrust.certArray as [AnyObject],
            persistence: NSURLCredentialPersistence.ForSession);

        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, urlCredential);

    } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {

        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(trust: challenge.protectionSpace.serverTrust!));

    } else {

        challenge.sender?.continueWithoutCredentialForAuthenticationChallenge(challenge)
        Logger.sharedInstance.logMessage("Unexpected Authentication Challange", .Error);

    }

【问题讨论】:

    标签: ios swift ssl swift2


    【解决方案1】:

    那么您的模拟器很可能正在运行 iOS 9。在 iOS 9 中,强制执行 TLS 1.2。如果您不使用它,您的请求将失败。

    查看this post 了解更多信息。

    您可以通过将其放入您的 Info.plist 来绕过它:

    <key>NSAppTransportSecurity</key>
    <dict>
      <!--Include to allow all connections (DANGER)-->
      <key>NSAllowsArbitraryLoads</key>
          <true/>
    </dict>
    

    但这只是一种临时解决方法,直到您可以实施 TLS 1.2。

    【讨论】:

    • 谢谢。只是一个(可能很明显的评论——对于像我这样的相对新手来说)——把它放在文件的底部,小心在它后面还有最后两行, 和 。我第一次尝试将其放置在顶部附近会破坏 XML 格式(如果放置不正确,您将收到错误消息“Info.plist 格式不正确”)。
    【解决方案2】:

    您没有绕过 TLS 层。请在下面找到使用自签名证书对我有用的答案。

    以下是适用于自签名 SSL 证书的代码更改

      func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    
        if challenge.protectionSpace.authenticationMethod == (NSURLAuthenticationMethodServerTrust) {
    
    
        let serverTrust:SecTrustRef = challenge.protectionSpace.serverTrust!
        let certificate: SecCertificateRef = SecTrustGetCertificateAtIndex(serverTrust, 0)!
        let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
        let cerPath: String = NSBundle.mainBundle().pathForResource("xyz.com", ofType: "cer")!
        let localCertificateData = NSData(contentsOfFile:cerPath)!
    
    
            if (remoteCertificateData.isEqualToData(localCertificateData) == true) {
                let credential:NSURLCredential = NSURLCredential(forTrust: serverTrust)
    
                challenge.sender?.useCredential(credential, forAuthenticationChallenge: challenge)
    
    
                completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
    
            } else {
    
                completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil)
            }
        }
        else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate
        {
    
            let path: String = NSBundle.mainBundle().pathForResource("client", ofType: "p12")!
            let PKCS12Data = NSData(contentsOfFile:path)!
    
    
            let identityAndTrust:IdentityAndTrust = self.extractIdentity(PKCS12Data);
    
    
    
                let urlCredential:NSURLCredential = NSURLCredential(
                    identity: identityAndTrust.identityRef,
                    certificates: identityAndTrust.certArray as? [AnyObject],
                    persistence: NSURLCredentialPersistence.ForSession);
    
                completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, urlCredential);
    
    
    
    
        }
        else
        {
            completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil);
        }
    }
    
     struct IdentityAndTrust {
    
        var identityRef:SecIdentityRef
        var trust:SecTrustRef
        var certArray:AnyObject
    }
    
    func extractIdentity(certData:NSData) -> IdentityAndTrust {
        var identityAndTrust:IdentityAndTrust!
        var securityError:OSStatus = errSecSuccess
    
        let path: String = NSBundle.mainBundle().pathForResource("client", ofType: "p12")!
        let PKCS12Data = NSData(contentsOfFile:path)!
        let key : NSString = kSecImportExportPassphrase as NSString
        let options : NSDictionary = [key : "xyz"]
        //create variable for holding security information
        //var privateKeyRef: SecKeyRef? = nil
    
        var items : CFArray?
    
         securityError = SecPKCS12Import(PKCS12Data, options, &items)
    
        if securityError == errSecSuccess {
            let certItems:CFArray = items as CFArray!;
            let certItemsArray:Array = certItems as Array
            let dict:AnyObject? = certItemsArray.first;
            if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> {
    
                // grab the identity
                let identityPointer:AnyObject? = certEntry["identity"];
                let secIdentityRef:SecIdentityRef = identityPointer as! SecIdentityRef!;
                print("\(identityPointer)  :::: \(secIdentityRef)")
                // grab the trust
                let trustPointer:AnyObject? = certEntry["trust"];
                let trustRef:SecTrustRef = trustPointer as! SecTrustRef;
                print("\(trustPointer)  :::: \(trustRef)")
                // grab the cert
                let chainPointer:AnyObject? = certEntry["chain"];
                identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray:  chainPointer!);
            }
        }
        return identityAndTrust;
    }
    

    在 info.plist 文件中所做的更改

         <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>amazonaws.com.cn</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSThirdPartyExceptionMinimumTLSVersion</key>
                <string>TLSv1.0</string>
            </dict>
            <key>amazonaws.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSThirdPartyExceptionMinimumTLSVersion</key>
                <string>TLSv1.0</string>
            </dict>
            <key>xyz.com</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSTemporaryExceptionMinimumTLSVersion</key>
                <string>TLSv1.2</string>
                <key>NSRequiresCertificateTransparency</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <true/>
            </dict>
        </dict>
        <key>NSAllowsArbitraryLoads</key>
        <false/>
    </dict>
    </plist>
    

    【讨论】:

      【解决方案3】:

      我在 iOS9 和 xcode 7 上遇到了同样的问题只需添加:

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

      到 plist 文件。它正在工作,但在您可以实施 TLS 1.2 之前,这是一个临时解决方法。

      【讨论】:

      • 以上代码行将绕过 TLS 安全层,如果您正在处理客户端安全信息通过网络传输的应用程序,这是不可接受的。如果在支付相关或客户个人信息所在的应用程序中使用该应用程序,Apple 可能会拒绝应用程序。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-19
      • 1970-01-01
      • 2012-08-22
      • 1970-01-01
      • 2013-09-29
      • 2018-03-01
      相关资源
      最近更新 更多