如何使CRL和OSCP检查在iOS上运行?

Uno*_*ome 7 security certificate certificate-revocation ios swift

我无法在iOS上使用CRL。我创建了两个测试用例。我有由CA颁发的有效证书。我有由CA颁发的另一个有效证书,但是CA已将该证书添加到其CRL中。

然后,我设置了一个启用CRL检查并要求成功的吊销策略。

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
}
Run Code Online (Sandbox Code Playgroud)

我的期望是CRL上的证书将不受信任,而干净的证书将受到信任。

在上述配置的情况下,两者均会由于不受信任而失败。如果删除该kSecRevocationRequirePositiveResponse标志,则两者均成功。我已经尝试过仅使用OSCP或仅使用CRL的所有不同排列方式,但没有一种能达到我期望的效果。

Apple 的州文件SecPolicyCreateRevocation

除非您希望覆盖默认的系统行为(例如强制使用特定方法或完全禁用吊销检查),否则通常无需自己创建吊销策略。

仅使用该SecPolicyCreateBasicX509策略就可以使两者都成功(当第二个证书应该失败时),那么Apple的默认行为是根本不进行CRL检查吗?

我将CharlesProxy附加到我的设备上,并在侦听所有网络流量的同时多次运行该代码,并且没有出站请求发送到CRL,这解释了为什么RequirePositiveResponse选中该标志时所有请求都失败。

我还尝试使用进行从设备直接导航到CRL的操作URLRequest,并且能够毫无问题地获取设备上的CRL数据。

Apple安全性库不支持CRL检查吗?如果是的话,是否有人知道配置以使其正确响应?我使用什么替代方法来进行CRL验证,我假设在金融区或其他敏感地区进行交易的高安全性移动应用程序将无法覆盖这一覆盖范围。

更新 为了进行比较,我certutil -f -urlfetch -verify MYCERT.cer使用certutil运行,并将 Fiddler连接到运行命令的框。我收到了iOS无法提供的预期结果,并且看到了通过提琴手通过HTTP向CRL发出的出站请求。

我创建了一个赏金计划,以引起更多对此的兴趣。我希望有人对上面做错了什么或为什么在iOS上不起作用有更多详细信息。

Ste*_*cht 14

在 Apple 平台上,客户端既不检查 CA 的证书吊销列表 (CRL),也不默认使用 OCSP。

然而,Apple 平台支持OCSP 装订,并且它们提供了一种称为Revocation Enhancement的替代机制,这确实可能导致 OCSP 调用,我将在下面展示。

OCSP 装订

首先解释OCSP装订:

在线证书状态协议 (OCSP) 装订,正式称为TLS 证书状态请求扩展,是检查 X.509 数字证书吊销状态的标准。1它允许证书的提交者通过在初始 TLS 握手中附加(“装订”)由 CA 签名的带时间戳的 OCSP 响应来承担提供在线证书状态协议 (OCSP) 响应所涉及的资源成本,从而消除了需要供客户联系 CA,以提高安全性和性能。

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

OCSP 和 OCSP 装订的区别

如果客户端在传统 OCSP 流中连接到服务器并检索证书,它会通过向 CA 发出请求来检查收到的证书是否已被吊销。这有一些缺点:例如,需要额外的网络连接,信息未加密,因此存在数据隐私问题。

通过 OCSP stapling,服务器向 CA 请求签名撤销信息并将其添加到 TLS 握手中。

这也意味着在使用 OCSP 装订时,您看不到从 iOS 到 CA 服务器的 OCSP 请求。

OCSP 装订的缺点

您要连接的服务器必须支持 OCSP 装订。这也不能防止恶意服务器。

这些是 Apple 提供吊销增强功能的主要原因。

Apple 的撤销增强功能

这是它的工作原理:

  • 证书透明度日志条目由 Apple 收集
  • 有了这些信息,Apple 就会从 CA 收集有关撤销的信息
  • 然后,这些汇总信息会定期自动提供给所有 Apple 客户
  • 基于此信息,当 iOS 应用程序尝试连接到带有吊销证书的服务器时,它会通过 OCSP 执行额外检查。

要求

应用程序支持此功能的唯一要求是将使用的服务器证书添加到证书透明度日志中。公共证书颁发机构可能已经这样做了,但您应该检查域证书是否在公共证书的活动透明度日志中,例如使用以下链接:https : //transparencyreport.google.com/https/certificates

WWDC 2017,第 701 届会议

有一个很好的 WWDC session,其中详细解释了这个主题和 Apple 的动机:WWDC 2017,session 701:https : //developer.apple.com/videos/play/wwdc2017/701/

大约 12:10 分钟,一位 Apple 工程师详细解释了整个撤销主题。在 15:30 左右,她解释说正常的 OCSP 需要使用额外的 API。

iOS上OCSP Stapling的测试

为了测试,我们需要一个支持 OCSP 装订并使用吊销证书的 服务器:例如https://revoked.grc.com(在这个 serverfault 答案中找到这个服务器:https : //serverfault.com/a/645066

然后我们可以尝试从 iOS 连接一个小的测试程序,该程序尝试下载 HTML 响应并将其输出到控制台。

根据上面提到的 WWDC 会话的信息,连接尝试应该会失败:

...
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()
}
Run Code Online (Sandbox Code Playgroud)

如果我们在 iOS 模拟器中执行上述例程,我们可以使用 Wireshark 来检查由 CA 签名的带时间戳的 OCSP 响应是否被装订到 TLS 握手。

通过nslookup revoked.grc.com我们获取服务器的 IP 地址,并可以在 Wireshark 中使用ip.addr==4.79.142.205.

在屏幕截图中,可以看到证书的状态为revoked

线鲨

因此,查看 Xcode 控制台,可以看到以下输出:

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}
Run Code Online (Sandbox Code Playgroud)

iOS 因 TLS 错误中止连接到服务器的尝试。

测试revoked.badssl.com

revoked.badssl.com 不支持 OCSP 装订。

如果我们查看https://revoked.badssl.com的证书详细信息,我们会发现:

如果下载 .crl 文件 (2.5MB) 并运行

openssl crl -inform DER -text -in ssca-sha2-g6.crl | grep 0371B58A86F6CE9C3ECB7BF42F9208FC
Run Code Online (Sandbox Code Playgroud)

可以看出,该证书确实是通过 CRL 吊销的。

有趣的是,Safari、Chrome 和 iOS 都无法识别这种撤销状态。只有 Mozilla Firefox 会显示错误消息(Peer 的证书已被撤销。错误代码: SEC_ERROR_REVOKED_CERTIFICATE)。

原因可能是证书仅在几天前更新,因此尚未进入所有浏览器和操作系统的本地撤销列表。