OCSP requests do not follow NSURLProtocol subclassing. Therefore OCSP requests are not proxied when using an NSURLProtocol subclass to proxy all traffic.
Our current workaround is to inspect the X.509v3 certificate being authenticated for OCSP URLs and then make the requests ourselves; subsequently attaching the response with SecTrustSetOCSPResponse(). Reference: https://github.com/Psiphon-Labs/OCSPCache.
Our Solution
In our implementation we do the following:
Check if there is a pinned OCSP response
Call SecTrustEvaluate with revocation policy SecPolicyCreateRevocation(kSecRevocationOCSPMethod|kSecRevocationRequirePositiveResponse|kSecRevocationNetworkAccessDisabled)
If SecTrustEvaluate succeeds, assume there was a pinned OCSP response
Make OCSP request manually through local proxy
Inspect leaf X.509v3 certificate of serverTrust in URLSession:task:didReceiveChallenge:completionHandler: for OCSP URLs
Make OCSP requests and attach OCSP response to trust
Call SecTrustEvaluate with revocation policy SecPolicyCreateRevocation(kSecRevocationOCSPMethod|kSecRevocationRequirePositiveResponse|kSecRevocationNetworkAccessDisabled)
We are only attaching the OCSP response for the leaf certificate being authenticated. Will plaintext OCSP requests be made by the system for intermediate certificates?
---
Note: There are tests in https://github.com/Psiphon-Labs/OCSPCache which shed some light on implementation details and expected behaviour.
-
Product Version: iOS 8+
Created: 2019-07-09T15:46:04.950021
Originated: 2019-07-09T00:00:00
Open Radar Link: http://www.openradar.me/716337334
Description
DESCRIPTION OF PROBLEM
OCSP requests do not follow NSURLProtocol subclassing. Therefore OCSP requests are not proxied when using an NSURLProtocol subclass to proxy all traffic.
Issue in more detail: https://github.com/OnionBrowser/OnionBrowser/issues/178.
Our current workaround is to inspect the X.509v3 certificate being authenticated for OCSP URLs and then make the requests ourselves; subsequently attaching the response with
SecTrustSetOCSPResponse()
. Reference: https://github.com/Psiphon-Labs/OCSPCache.Our Solution
In our implementation we do the following:
Check if there is a pinned OCSP response
SecPolicyCreateRevocation(kSecRevocationOCSPMethod|kSecRevocationRequirePositiveResponse|kSecRevocationNetworkAccessDisabled)
SecTrustEvaluate
succeeds, assume there was a pinned OCSP responseMake OCSP request manually through local proxy
URLSession:task:didReceiveChallenge:completionHandler:
for OCSP URLsSecTrustEvaluate
with revocation policySecPolicyCreateRevocation(kSecRevocationOCSPMethod|kSecRevocationRequirePositiveResponse|kSecRevocationNetworkAccessDisabled)
Note: Since we are caching OCSP responses, but do not know when iOS considers responses valid/invalid we only evict responses when
SecTrustEvaluate
fails with an OCSP response fetched out of band. If the response we attach was cached then we make another remote request for a new response. This can be seen here: https://github.com/Psiphon-Labs/OCSPCache/blob/b945a5784cd88ed5693a62a931617bd371f3c9a8/OCSPCache/Classes/OCSPAuthURLSessionDelegate.m#L196-L216.Questions
Is the assumption above regarding pinned OCSP responses correct?
Are the revocation checks in https://github.com/Psiphon-Labs/OCSPCache/blob/b945a5784cd88ed5693a62a931617bd371f3c9a8/OCSPCache/Classes/OCSPAuthURLSessionDelegate.m#L138 performed correctly?
We are only attaching the OCSP response for the leaf certificate being authenticated. Will plaintext OCSP requests be made by the system for intermediate certificates?
STEPS TO REPRODUCE
Create NSURLProtocol subclass which proxies all traffic through a SOCKS or HTTP proxy by setting connection proxy dictionary (https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1411499-connectionproxydictionary?language=objc)
SecTrustResultType trustResultType; SecTrustEvaluate(trust, &trustResultType); // triggers plaintext OCSP request outside of proxy