Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get CRL and OSCP Checking to work on iOS?

I can't get CRLs working on iOS. I've created two test cases. I have a certificate that is valid, issued by a CA. I have another certificate that is valid, issued by a CA, but the CA has added that certificate to its CRL.

I then setup a revocation policy that enables CRL checking, and requires that it succeeds.

func crlValidationTest(trustedCert: SecCertificate, certToVerify: SecCertificate) -> Bool {

    let basicPolicy = SecPolicyCreateBasicX509()

    let crlPolicy = SecPolicyCreateRevocation(kSecRevocationOCSPMethod | kSecRevocationCRLMethod | kSecRevocationRequirePositiveResponse)!

    var trust: SecTrust?

    SecTrustCreateWithCertificates(NSArray(object: certToVerify), NSArray(objects: basicPolicy, crlPolicy), &trust)
    SecTrustSetAnchorCertificates(trust!, NSArray(object: trustedCert))
    SecTrustSetNetworkFetchAllowed(trust!, true)

    var trustResult = SecTrustResultType.invalid

    guard SecTrustEvaluate(trust!, &trustResult) == errSecSuccess else {
        return false
    }

    return trustResult == SecTrustResultType.proceed || trustResult == SecTrustResultType.unspecified
}

My expectation is that the certificate that is on the CRL will be untrusted, and the certificate that is clean will be trusted.

Given the above configuration, both fail as untrusted. If I remove the kSecRevocationRequirePositiveResponse flag, both succeed. I've tried all the different permutations of using only OSCP or only CRL, and nothing works the way I would expect.

Apples documentation for SecPolicyCreateRevocation states:

It's usually not necessary to create a revocation policy yourself unless you wish to override default system behavior, for example to force a particular method, or to disable revocation checking entirely.

Using only the SecPolicyCreateBasicX509 policy allows both to succeed (when the second cert should fail), so is Apple's default behavior not to do CRL checking at all?

I attached CharlesProxy to my device, and ran the code multiple times while listening to all network traffic, and no outbound requests ever go to the CRL which explains why all fail when the RequirePositiveResponse flag is checked.

I also tried navigating directly from the device to the CRL using a URLRequest, and was able to get the CRL data on device without any issues.

Is CRL checking not supported via the Apple Security library? If it is, has anyone figured out the configuration to get it to respond correctly? What alternatives are bing used to do CRL validation, I'm assuming high-security mobile applications dealing in the financial district or other sensitive areas would not allow this coverage gap.

UPDATE For comparison, I ran certutil -f -urlfetch -verify MYCERT.cer using certutil, and I attached Fiddler to the box running the command. I receive the expected results that iOS is not giving me, and I see an outbound request to the CRL via HTTP through fiddler.

I've created a bounty to generate some more interest in this. I'm hoping someone has more details as to what is being done wrong above, or why this doesn't work on iOS.

like image 744
Unome Avatar asked Oct 03 '19 22:10

Unome


People also ask

How do I verify CRL?

To check the status of a certificate using a CRL, the client reaches out to the CA (or CRL issuer) and downloads its certificate revocation list. After doing this, it then must search through the entire list for that individual certificate.


1 Answers

On Apple platforms, clients neither check Certificate Revocation List (CRL) of CAs, nor do they use OCSP by default.

Apple platforms, however, support OCSP stapling and they provide an alternative mechanism they call Revocation Enhancement, which could indeed lead to an OCSP call, as I'll show below.

OCSP Stapling

First an explanation of OCSP stapling:

Online Certificate Status Protocol (OCSP) stapling, formally known as the TLS Certificate Status Request extension, is a standard for checking the revocation status of X.509 digital certificates.1 It allows the presenter of a certificate to bear the resource cost involved in providing Online Certificate Status Protocol (OCSP) responses by appending ("stapling") a time-stamped OCSP response signed by the CA to the initial TLS handshake, eliminating the need for clients to contact the CA, with the aim of improving both security and performance.

see https://en.wikipedia.org/wiki/OCSP_stapling

Differences between OCSP and OCSP Stapling

If a client connects to a server in a traditional OCSP flow and retrieves the certificate, it checks whether the certificate received has been revoked by making a request to the CA. This has some disadvantages: for example, an additional network connection is required, the information is unencrypted and therefore represents a data privacy problem.

Through OCSP stapling, the server requests signed revocation information from the CA and adds it to the TLS handshake.

This also means that when using OCSP stapling, you do not see an OCSP request from iOS to a CA server.

Drawbacks of OCSP Stapling

The server you are connecting to must support OCSP stapling. This also does not protect against malicious servers.

These are the main reasons Apple is providing Revocation Enhancement.

Apple's Revocation Enhancement

Here's how it works:

  • certificate transparency log entries are gathered by Apple
  • with this info, Apple gathers information about revocations from CAs
  • this aggregated information is then automatically made available to all Apple clients on a regular basis
  • based on this information, when an iOS app attempts to connect to a server with a revoked certificate, it performs an additional check via OCSP.

Requirement

The only requirement for an app to support this is that the server certificate used be added to a certificate transparency log. A public Certification Authority may do this already, but you should check that the domain certificate is in the active transparency logs for public certificates, e.g. by using the following link: https://transparencyreport.google.com/https/certificates

WWDC 2017, session 701

There is an excellent WWDC session in which this topic and Apple's motives are explained in detail: WWDC 2017, session 701: https://developer.apple.com/videos/play/wwdc2017/701/

Around 12:10 mins, an Apple engineer explains the entire revocation topic in detail. At around 15:30, she explains that normal OCSP would require the use of additional APIs.

Test of OCSP Stapling on iOS

For testing, we need a server that supports OCSP stapling and uses a revoked certificate: e.g. https://revoked.grc.com (found this server in this serverfault answer: https://serverfault.com/a/645066)

Then we can try to connect from iOS with a small test program that tries to download a HTML response and output it to the console.

Based on the information from the WWDC session mentioned above, the connection attempt should fail:

...
let session = URLSession(configuration: .default)
...

func onDownloadAction() {
    let url = URL(string: "https://revoked.grc.com")!
    self.download(from: url) { (result, error) in
        if let result = result {
            print("result: " + result)
        } else {
            print("download failed")
            if let error = error {
                print("error: \(error)")
            }
        }
    }
}


func download(from url: URL, completion: @escaping(String?, Error?)->Void) {
    let dataTask = self.session.dataTask(with: url) { data, response, error in
        guard let data = data else {
            if let error = error {
                completion(nil, error)
                return
            }
            completion(nil, NSError(domain: "DownloadFailure", code: 0, userInfo:nil))
            return
        }

        guard let response = response as? HTTPURLResponse else {
            completion(nil, NSError(domain: "ResponseFailure", code: 0, userInfo:nil))
            return
        }
        print("http status: \(response.statusCode)")
        let res = String(bytes: data, encoding: .utf8)
        completion(res, nil)
    }
    dataTask.resume()
}

If we execute the above routine in the iOS Simulator, we can use Wireshark to check whether a time-stamped OCSP response signed by the CA is stapled to the TLS handshake.

With nslookup revoked.grc.com we get the IP address of the server and can filter in Wireshark with ip.addr==4.79.142.205.

In the screenshot, one can see that the certificate has the status revoked.

wireshark

So, taking a look into the Xcode console, one can see the following output:

2019-10-12 21:32:25.734382+0200 OCSPTests[6701:156558] ATS failed system trust
2019-10-12 21:32:25.734526+0200 OCSPTests[6701:156558] Connection 1: system TLS Trust evaluation failed(-9802)
2019-10-12 21:32:25.734701+0200 OCSPTests[6701:156558] Connection 1: TLS Trust encountered error 3:-9802
2019-10-12 21:32:25.734787+0200 OCSPTests[6701:156558] Connection 1: encountered error(3:-9802)
2019-10-12 21:32:25.737672+0200 OCSPTests[6701:156558] Task <12408947-689F-4537-9642-C8F95E86CA62>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9802])
download failed
error: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x6000037f8510>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
    "<cert(0x7fda78828200) s: revoked.grc.com i: DigiCert SHA2 Secure Server CA>",
    "<cert(0x7fda7882b200) s: DigiCert SHA2 Secure Server CA i: DigiCert Global Root CA>"
), NSUnderlyingError=0x600000be9170 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x6000037f8510>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
    "<cert(0x7fda78828200) s: revoked.grc.com i: DigiCert SHA2 Secure Server CA>",
    "<cert(0x7fda7882b200) s: DigiCert SHA2 Secure Server CA i: DigiCert Global Root CA>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://revoked.grc.com/, NSErrorFailingURLStringKey=https://revoked.grc.com/, NSErrorClientCertificateStateKey=0}

iOS aborts the attempt to connect to the server with a TLS error.

Test revoked.badssl.com

revoked.badssl.com does not support OCSP stapling.

If we take a look at the certificate details of https://revoked.badssl.com, we find out:

  • it has the serial number: 0371B58A86F6CE9C3ECB7BF42F9208FC
  • the CRL URL is: http://crl3.digicert.com/ssca-sha2-g6.crl

If one downloads the .crl file (2.5MB) and runs

openssl crl -inform DER -text -in ssca-sha2-g6.crl | grep 0371B58A86F6CE9C3ECB7BF42F9208FC

one can see that this certificate is indeed revoked via CRL.

Interestingly, neither Safari nor Chrome nor iOS recognize this revoked status. Only Mozilla Firefox displays an error message (Peer’s Certificate has been revoked. Error code: SEC_ERROR_REVOKED_CERTIFICATE).

The reason might be that the certificate was renewed only a few days ago and has therefore not yet found its way into all local revoke lists of browsers and operating systems.

like image 181
Stephan Schlecht Avatar answered Oct 08 '22 11:10

Stephan Schlecht