From what I can tell, when using client certificate authentication with NSURLSession, it is impossible to get the actual server response if the supplied client certificate was not authorized on the server. Based on what I have found, if you supply a client certificate credential in -URLSession:task:didReceiveChallenge:completionHandler: and the server replies with a 403, then the -URLSession:task:didCompleteWithError: delegate method get called with an NSURLErrorClientCertificateRequired error.
My app needs to see the actual headers that the server responded. I know that a valid response is being sent because I can see it in Wireshark and in CFNETWORK_DIAGNOSTICS output. The response property of the task object passed into -URLSession:task:didCompleteWithError: is nil so I can't use that. Is there anyway at all that I can get the response object in this circumstance or can anyone think of a workaround here?
Thanks,
Dustin
I dug into this issue as part of a DTS incident (s. 656775591) and my final conclusion was:
There’s no direct answer to this problem, so the other developer ended up filing an enhancement request about this (r. 29992970). Feel free to file your own bug report describing your own specific requirements here.
The
error you see in this case is triggered by a specific sequence of events:NSURLErrorClientCertificateRequired
If the request fails with 403
And the delegate responded to the client identity authentication challenge
And the app has multiple digital identities in its keychain (this criteria is only applied on macOS; on iOS-based platforms it’s assumed to be true)
Fail the request with
NSURLErrorClientCertificateRequired
Otherwise, deliver the 403 response
If you have any control over the server then a good way to approach this issue is to have it set the
field in the CertificateRequest message (see Section 7.4.4 of RFC 5246). You can get this from the client identity authentication challenge (via the NSURLProtectionSpace’scertificate_authorities
property) and use this to guide the user’s certificate choice.distinguishedNames
If not, your workaround options are rather limited. The best I could come up with was to trap the
error and then run a dummy request via CFHTTPStream, supplying the same digital identity as you used for the NSURLSession request. That should let you get at the 403 response, at which point you can retry the request with NSURLSession, using the 403 response to guide your digital identity choice.NSURLErrorClientCertificateRequired
IMPORTANT CFHTTPStream is missing a bunch of smarts that you’re used to from NSURLSession. Many of these don’t matter in this case (authentication challenges, caching, cookies) but there’s one that might: proxies. If you expect your app to run in an environment that uses proxies, you’ll have to get the current proxy configuration (via
<CFNetwork/CFProxySupport.h>
) and apply it to your CFHTTPStream.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"