I´m trying to implement mTLS with URLSession in an iOS App.
Currently the connection is working on some paths (for example “https://xxxx.yyy/api/users”) and i get the correct status 200 back.
Now I tried to reach “https://xxxx.yyy/api/users/” (with the same Session) and the returned response was
Task <xxxxx>.<3> finished with error [-1206] Error Domain=NSURLErrorDomain Code=-1206 "The server “xxxx.yyy” requires a client certificate."
Then I tried the same with an another dev client (Postman) to test if the server was the problem and there it worked.
The url is called from:
let usedURL = URL(string: "https://xxxx.yyy/api/users/")!
var request = URLRequest(url: usedURL)
request.httpMethod = "GET"
let (data, response) = try await sessionDelegate.getSession().data(for: request)
let statusCode = (response as? HTTPURLResponse)?.statusCode
if statusCode != 200 {
Logger.shared.error("Token request response was not 200 but (statusCode ?? -1, privacy: .public), (String(decoding: data, as: UTF8.self))")
if statusCode == 400 {
errorController.push(error: LoginError.invalidLogin)
}
return
}
and the SessionDelegate
class SessionDelegate: NSObject, URLSessionDelegate {
private var certData: Data? = nil
private var certPass: String? = nil
var session: URLSession? = nil
public func getSession() -> URLSession {
if session == nil {
self.session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
}
return session!
}
public func setCertPass(certPass: String) -> Void {
self.certPass = certPass
}
public func setCertData(certData: Data) -> Void {
self.certData = certData
}
public func getCertData() -> Data? {
return certData
}
public func getCertPass() -> String? {
return certPass
}
func testUrl(url: String) async -> String{
do {
let (_, response) = try await getSession().data(from: URL(string: url)!)
guard let httpResponse = response as? HTTPURLResponse else { return "" }
return "HTTP Status Code (httpResponse.statusCode)"
} catch {
return error.localizedDescription
}
}
public func loadCertData(data: Data) -> Bool{
certData = data
return true
}
public func urlSession(_: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.protectionSpace.authenticationMethod
== NSURLAuthenticationMethodClientCertificate
else {
print("Requied other url auth: (challenge.protectionSpace.authenticationMethod)")
completionHandler(.performDefaultHandling, nil)
return
}
print("Using Delegate")
guard
let p12Data = certData
else {
print("CertData not loaded")
completionHandler(.performDefaultHandling, nil)
return
}
guard
let pass = certPass
else {
print("CertPass not loaded")
completionHandler(.performDefaultHandling, nil)
return
}
do {
let p12Contents = try PKCS12(pkcs12Data: p12Data, password: pass)
guard let identity = p12Contents.identity else {
print("Error loading identity")
completionHandler(.performDefaultHandling, nil)
return
}
let credential = URLCredential(identity: identity, certificates: nil, persistence: .none)
challenge.sender?.use(credential, for: challenge)
completionHandler(.useCredential, credential)
print("Client auth complete")
return
}catch {
print(error)
completionHandler(.performDefaultHandling, nil)
}
}
}
I think i narrowed down to the trailing “/”.
I am pretty new to the iOS and Swift world, so maybe I´m missing something 😉
Nils Witt is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.