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")
}
感谢您的回答,但我不想做证书锁定,我只是想添加一个新的CA根证书,默认情况下不在iOS上加载。 – Stephan
并感谢您的Firefox提示! – Stephan