异常信息
没有客户端校验 (解决方式配置 info.plist)
An SSL error has occurred and a secure connection to the server cannot be made.
URLSessionDelegate 证书认证!
URLSessionDelegate 服务端证书认证!
URLSessionDelegate 服务端证书认证! is equal
2023-03-15 16:33:21.470219+0800 chat[4748:412091] ATS failed system trust
2023-03-15 16:33:21.470308+0800 chat[4748:412091] Connection 1: system TLS Trust evaluation failed(-9802)
2023-03-15 16:33:21.470447+0800 chat[4748:412091] Connection 1: TLS Trust encountered error 3:-9802
2023-03-15 16:33:21.470514+0800 chat[4748:412091] Connection 1: encountered error(3:-9802)
2023-03-15 16:33:21.472186+0800 chat[4748:412091] Task <AFA49683-E1F5-445C-BD56-BF6EA6341C34>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9802])
2023-03-15 16:33:21.472856+0800 chat[4748:412091] [tcp] tcp_output [C1.1.1:3] flags=[R.] seq=1741325076, ack=1322275982, win=4093 state=CLOSED rcv_nxt=1322275982, snd_una=1741325076
2023-03-15 16:33:21.524408+0800 chat[4748:411894] Task <AFA49683-E1F5-445C-BD56-BF6EA6341C34>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
"<cert(0x10800c200) s: chat.api.com i: chat.api.com>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://chat.api.com/message/joined, NSErrorFailingURLStringKey=https://chat.api.com/message/joined, NSUnderlyingError=0x282b60420 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x2814081e0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x10800c200) s: chat.api.com i: chat.api.com>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <AFA49683-E1F5-445C-BD56-BF6EA6341C34>.<1>"
), _kCFStreamErrorCodeKey=-9802, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <AFA49683-E1F5-445C-BD56-BF6EA6341C34>.<1>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x2814081e0>, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.}
完整代码
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.systemTeal
//双向认证
httpGet(request: URLRequest(url: URL(string: "https://chat.api.com/message/joined")!))
}
}
// MARK: URLSessionDelegate
extension ViewController: URLSessionDelegate {
// 使用URLSession请求数据
func httpGet(request: URLRequest) {
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue:OperationQueue.main)
let dataTask = session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
if let data = data, let str = String(data: data, encoding: String.Encoding.utf8) {
print("URLSessionDelegate 访问成功,数据:" + str)
}
print("URLSessionDelegate error:" + (error?.localizedDescription ?? "none"))
})
//使用resume方法启动任务
dataTask.resume()
}
// 在访问资源的时候,如果服务器返回需要授权(提供一个URLCredential对象)
// 那么该方法就回被调用(这个是URLSessionDelegate代理方法)
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print("URLSessionDelegate 证书认证!")
//认证服务器证书
if challenge.protectionSpace.authenticationMethod == (NSURLAuthenticationMethodServerTrust) {
print("URLSessionDelegate 服务端证书认证!")
guard let serverTrust:SecTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0),
let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate)) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
guard let cerPath = Bundle.main.path(forResource: "client_trust", ofType: "cer") else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let cerUrl = URL(fileURLWithPath:cerPath)
guard let localCertificateData = try? Data(contentsOf: cerUrl) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
if (remoteCertificateData.isEqual(localCertificateData)) {
let credential = URLCredential(trust: serverTrust)
challenge.sender?.use(credential, for: challenge)
completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential)
} else {
//completionHandler(.cancelAuthenticationChallenge, nil)
completionHandler(.performDefaultHandling, nil)
}
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
//认证客户端证书
print("URLSessionDelegate 客户端证书认证!")
//获取客户端证书相关信息
let identityAndTrust:IdentityAndTrust = self.extractIdentity()
let urlCredential:URLCredential = URLCredential(identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certArray as? [AnyObject],
persistence: URLCredential.Persistence.forSession)
completionHandler(.useCredential, urlCredential)
} else {
// 其它情况(不接受认证)
print("URLSessionDelegate 其它情况(不接受认证)")
completionHandler(.cancelAuthenticationChallenge, nil);
}
}
//获取客户端证书相关信息
func extractIdentity() -> IdentityAndTrust {
print("URLSessionDelegate 获取客户端证书相关信息")
var identityAndTrust:IdentityAndTrust!
var securityError:OSStatus = errSecSuccess
let path: String = Bundle.main.path(forResource: "client", ofType: "p12")!
let PKCS12Data = NSData(contentsOfFile:path)!
let key = kSecImportExportPassphrase as NSString
let options: NSDictionary = [key : "123456"] //客户端证书密码
//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 {
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:SecIdentity = identityPointer as! SecIdentity
print("URLSessionDelegate \(identityPointer) :::: \(secIdentityRef)")
// grab the trust
let trustPointer:AnyObject? = certEntry["trust"]
let trustRef:SecTrust = trustPointer as! SecTrust
print("URLSessionDelegate \(trustPointer) :::: \(trustRef)")
// grab the cert
let chainPointer:AnyObject? = certEntry["chain"]
identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef,
trust: trustRef, certArray: chainPointer!)
}
}
return identityAndTrust;
}
}
//定义一个结构体,存储认证相关信息
struct IdentityAndTrust {
var identityRef:SecIdentity
var trust:SecTrust
var certArray:AnyObject
}
info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>chat.api.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</dict>
客户端证书另一种实现 ( 异常信息是业务错误
)
The server “chat.api.com” requires a client certificate.
URLSessionDelegate 证书认证!
URLSessionDelegate 服务端证书认证!
URLSessionDelegate 证书认证!
URLSessionDelegate 客户端证书认证!
URLSessionDelegate 获取客户端证书相关信息
URLSessionDelegate Optional(<SecIdentityRef: 0x2833eda00>) :::: <SecIdentityRef: 0x2833eda00>
URLSessionDelegate Optional(<SecTrustRef: 0x2802c41e0>) :::: <SecTrustRef: 0x2802c41e0>
2023-03-15 17:35:31.813096+0800 aichat[5173:433850] Task <B7354C74-EB9D-4EEE-BE58-617E5B83F408>.<1> finished with error [-1206] Error Domain=NSURLErrorDomain Code=-1206 "The server “chat.api.com” requires a client certificate." UserInfo={NSLocalizedDescription=The server “chat.api.com” requires a client certificate., NSErrorFailingURLStringKey=https://chat.api.com/message/joined, NSErrorFailingURLKey=https://chat.api.com/message/joined, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <B7354C74-EB9D-4EEE-BE58-617E5B83F408>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <B7354C74-EB9D-4EEE-BE58-617E5B83F408>.<1>, NSUnderlyingError=0x283db06c0 {Error Domain=kCFErrorDomainCFNetwork Code=-1206 "(null)" UserInfo={NSErrorPeerAddressKey=<CFData 0x2810f4690 [0x1fbf7b6c8]>{length = 16, capacity = 16, bytes = 0x100201bb2b8b8de80000000000000000}}}}
URLSessionDelegate error:The server “chat.api.com” requires a client certificate.
2023-03-15 17:35:33.413266+0800 aichat[5173:433885] [connection] nw_resolver_start_query_timer_block_invoke [C1.1] Query fired: did not receive all answers in time for chat.api.com:443
修改 NSURLAuthenticationMethodClientCertificate
校验
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
//认证客户端证书
print("URLSessionDelegate 客户端证书认证!")
//获取项目中P12证书文件的路径
let path: String = Bundle.main.path(forResource: "client", ofType: "p12")!
let PKCS12Data = NSData(contentsOfFile:path)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "123456"] //客户端证书密码
var items: CFArray?
let error = SecPKCS12Import(PKCS12Data, options, &items)
if error == errSecSuccess {
let itemArr = items! as Array
let item = itemArr.first!
let identityPointer = item["identity"];
let secIdentityRef = identityPointer as! SecIdentity
let chainPointer = item["chain"]
let chainRef = chainPointer as? [Any]
let credential = URLCredential.init(identity: secIdentityRef, certificates: chainRef, persistence: URLCredential.Persistence.forSession)
challenge.sender?.use(credential, for: challenge)
completionHandler(.useCredential, credential)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
网友评论