EXC_BAD_ACCESS SecTrustEvaluate调用Swift 2.0时加载自己的CA证书

问题描述:

我正在加载我自己的根证书作为我的应用程序的锚证书,但我总是得到SecTrustEvaluate(trust!, &trustResult)行上的EXC_BAD_ACCESS。 任何人都可以看到我做错了什么?EXC_BAD_ACCESS SecTrustEvaluate调用Swift 2.0时加载自己的CA证书

我看过Always EXC_BAD_ACCESS on SecTrustEvaluate,但它不能解决我的问题,因为我认为我的可能是Swift特有的问题。

public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { 
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 
     // First load our extra root-CA to be trusted from the app bundle. 
     let trust = challenge.protectionSpace.serverTrust 

     let rootCa = "SSLcomDVCA_2_DER" 
     if let rootCaPath = NSBundle.mainBundle().pathForResource(rootCa, ofType: "cer") { 
      if let rootCaData = NSData(contentsOfFile: rootCaPath) { 

       let cfData = CFDataCreate(kCFAllocatorDefault, UnsafePointer<UInt8>(rootCaData.bytes), rootCaData.length) 

       let rootCert = SecCertificateCreateWithData(nil, cfData) 

       let certs: [CFTypeRef] = [rootCert!] 
       let certPointer = UnsafeMutablePointer<UnsafePointer<Void>>(certs) 
       let certArrayRef = CFArrayCreate(nil, certPointer 
        , certs.count, nil) 
       SecTrustSetAnchorCertificates(trust!, certArrayRef) 
       SecTrustSetAnchorCertificatesOnly(trust!, false) // also allow regular CAs. 
      } 
     } 

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

     if (Int(trustResult) == kSecTrustResultUnspecified || 
      Int(trustResult) == kSecTrustResultProceed) { 
       // Trust certificate. 
       let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!) 
       challenge.sender!.useCredential(credential, forAuthenticationChallenge: challenge) 
     } else { 
      NSLog("Invalid server certificate.") 
      challenge.sender!.cancelAuthenticationChallenge(challenge) 
     } 
    } else { 
     NSLog("Got unexpected authentication method \(challenge.protectionSpace.authenticationMethod)"); 
     challenge.sender!.cancelAuthenticationChallenge(challenge) 
    } 
} 

最终解决方案如下。我最初的代码的问题是我尝试创建CFArray的方式。在代码中的某处:

let certs: [CFTypeRef] = [rootCert!] 
let certPointer = UnsafeMutablePointer<UnsafePointer<Void>>(certs) 
let certArrayRef = CFArrayCreate(nil, certPointer, certs.count, nil) 

,我失去了对证书的引用。通过将代码更改为:

let certs: [CFTypeRef] = [rootCert as! CFTypeRef] 
let certArrayRef : CFArrayRef = CFBridgingRetain(certs as NSArray) as! CFArrayRef 

,问题已排序。

func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { 
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 

     let trust = challenge.protectionSpace.serverTrust 

     let rootCa = "SSLcomDVCA_2_DER2" 
     if let rootCaPath = NSBundle.mainBundle().pathForResource(rootCa, ofType: "der") { 
      if let rootCaData: NSData = NSData(contentsOfFile: rootCaPath) { 

       let cfData = CFDataCreate(kCFAllocatorDefault, UnsafePointer<UInt8>(rootCaData.bytes), rootCaData.length) 

       let rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, cfData) 

       let certs: [CFTypeRef] = [rootCert as! CFTypeRef] 

       let certArrayRef : CFArrayRef = CFBridgingRetain(certs as NSArray) as! CFArrayRef 
       SecTrustSetAnchorCertificates(trust!, certArrayRef) 

       SecTrustSetAnchorCertificatesOnly(trust!, false) // also allow regular CAs. 
      } 
     } 

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

     if (Int(trustResult) == kSecTrustResultUnspecified || 
      Int(trustResult) == kSecTrustResultProceed) { 
       // Trust certificate. 
       let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!) 
       challenge.sender!.useCredential(credential, forAuthenticationChallenge: challenge) 
     } else { 
      NSLog("Invalid server certificate.") 
      challenge.sender!.cancelAuthenticationChallenge(challenge) 
     } 
    } else { 
     NSLog("Got unexpected authentication method \(challenge.protectionSpace.authenticationMethod)"); 
     challenge.sender!.cancelAuthenticationChallenge(challenge) 
    } 

使用Certificate and Public Key Pinning为指导,我想出了这个在斯威夫特的证书钉扎的实现。它与你的方法不同,但它在我的测试中起作用。

可以使用Firefox下载.der格式的证书,方法是进入网站,单击地址栏>更多信息>查看证书>详细信息选项卡>导出(从格式列表中选择DER)中的挂锁。

func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { 

    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 

     guard let serverTrust = challenge.protectionSpace.serverTrust else { 
      print("serverTrust is nil") 
      return 
     } 

     guard errSecSuccess == SecTrustEvaluate(serverTrust, nil) else { 
      print("SecTrustEvaluate is not errSecSuccess") 
      return 
     } 

     guard let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else { 
      print("serverCertificate is nil") 
      return 
     } 

     let serverCertificateData = SecCertificateCopyData(serverCertificate) 
     let data = CFDataGetBytePtr(serverCertificateData) 
     let size = CFDataGetLength(serverCertificateData) 
     let dataPtr = unsafeBitCast(data, UnsafeMutablePointer<Void>.self) 

     let cert1 = NSData(bytes: dataPtr, length: size) 

     guard let file = NSBundle.mainBundle().pathForResource("facebook.com", ofType: "der"), cert2 = NSData(contentsOfFile: file) else { 
      print("Failed to open .der file") 
      return 
     } 

     guard cert1 == cert2 else { 
      print("Certificate pinning failed, certs unequal") 
      return 
     } 

     // Good exit point. 
     print("PASSED Cert Pinning") 
     return challenge.sender!.useCredential(NSURLCredential(forTrust: serverTrust), forAuthenticationChallenge: challenge) 
    } 

    print("FAILED Cert Pinning, authenticationMethod not ServerTrust") 
} 
+0

感谢您的回答,但我不想做证书锁定,我只是想添加一个新的CA根证书,默认情况下不在iOS上加载。 – Stephan

+0

并感谢您的Firefox提示! – Stephan