web-dev-qa-db-ja.com

CRLおよびOCSPチェックをiOSで機能させるにはどうすればよいですか?

IOSでCRLを機能させることができません。 2つのテストケースを作成しました。 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
}

私の期待は、CRLにある証明書は信頼されず、クリーンな証明書は信頼されることです。

上記の構成では、どちらも信頼できないものとして失敗します。 kSecRevocationRequirePositiveResponseフラグを削除すると、どちらも成功します。私はOSCPのみまたはCRLのみを使用するさまざまな順列をすべて試しましたが、期待どおりに機能しません。

りんご ドキュメント for SecPolicyCreateRevocation状態:

特定のメソッドを強制したり、失効チェックを完全に無効にしたりするなど、デフォルトのシステム動作を上書きしたくない場合を除いて、通常、失効ポリシーを自分で作成する必要はありません。

SecPolicyCreateBasicX509ポリシーのみを使用すると、両方が成功するため(2番目の証明書が失敗した場合)、Appleのデフォルトの動作ではCRLチェックをまったく行わないのですか?

私は CharlesProxy をデバイスに接続し、すべてのネットワークトラフィックをリッスンしながらコードを複数回実行しましたが、RequirePositiveResponseフラグがすべて失敗した理由を説明する送信リクエストがCRLに送信されませんチェックされています。

また、URLRequestを使用してデバイスからCRLに直接移動してみましたが、デバイス上で問題なくCRLデータを取得できました。

Appleセキュリティライブラリを介してCRLチェックがサポートされていませんか?サポートされている場合、正しく応答するための構成を誰かが理解していますか?CRL検証を行うためにどの代替手段が使用されていますか?金融街やその他のデリケートなエリアを扱う高セキュリティのモバイルアプリケーションでは、このカバレッジギャップは許容されないと想定します。

[〜#〜] update [〜#〜]比較のために、certutilを使用してcertutil -f -urlfetch -verify MYCERT.cerを実行しました、コマンドを実行しているボックスに Fiddler を添付しました。 iOSが提供していないと予想される結果を受け取り、fiddlerを介してHTTP経由でCRLへのアウトバウンドリクエストを確認します。

私はこれにいくつかの関心を生み出すための賞金を作成しました。上記の何が間違っているのか、なぜこれがiOSで機能しないのかについて誰かが詳細を知っていることを願っています。

9
Unome

Appleプラットフォームでは、クライアントはCAの証明書失効リスト(CRL)をチェックせず、デフォルトでOCSPを使用しません。

ただし、AppleプラットフォームはOCSPステープリングをサポートしており、代わりに、失効拡張を呼び出すメカニズムを提供します。これにより、実際にOCSP呼び出しが発生する可能性があります。詳細は以下を参照してください。

OCSPホチキス止め

最初にOCSPステープリングの説明:

Online Certificate Status Protocol(OCSP)stapling(正式にはTLS Certificate Status Request拡張として知られています)は、X.509デジタル証明書の失効ステータスを確認するための標準です。- 1 CAによって署名されたタイムスタンプ付きのOCSP応答を最初に追加(「ステープル」)することにより、証明書のプレゼンターがオンライン証明書ステータスプロトコル(OCSP)応答の提供に関連するリソースコストを負担できるようにしますTLSハンドシェイクにより、セキュリティとパフォーマンスの両方を向上させることを目的として、クライアントがCAに連絡する必要がなくなります。

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

OCSPとOCSPステイプルの違い

クライアントが従来のOCSPフローでサーバーに接続して証明書を取得する場合、クライアントは、CAに要求を行うことによって、受信した証明書が取り消されているかどうかを確認します。これにはいくつかの欠点があります。たとえば、追加のネットワーク接続が必要であり、情報が暗号化されていないため、データのプライバシーの問題が発生します。

OCSPステープリングを通じて、サーバーはCAに署名された失効情報を要求し、それをTLSハンドシェイクに追加します。

これは、OCSPステープリングを使用している場合、iOSからCAサーバーへのOCSP要求が表示されないことも意味します。

OCSPステープリングの欠点

接続するサーバーは、OCSPステープリングをサポートしている必要があります。これは、悪意のあるサーバーからの保護も行いません。

Appleが失効強化を提供している主な理由です。

アップルの失効強化

仕組みは次のとおりです。

  • 証明書の透明性ログのエントリはAppleによって収集されます
  • この情報を使用してApple CAからの失効に関する情報を収集します
  • この集約された情報は、自動的にすべてのAppleクライアントが定期的に利用できるようになります
  • この情報に基づいて、iOSアプリが失効した証明書でサーバーに接続しようとすると、OCSPを介して追加のチェックが実行されます。

要件

アプリがこれをサポートするための唯一の要件は、使用されるサーバー証明書が証明書透過性ログに追加されることです。通常、CAは既にそれを行っていますが、ドメイン証明書がパブリック証明書のアクティブな透過性ログにあることを確認する必要があります。次のリンクを使用して: https://transparencyreport.google.com/https/certificates

WWDC 2017、セッション701

このトピックとAppleの動機が詳細に説明されている優れたWWDCセッションがあります。WWDC2017、セッション701: https://developer.Apple.com/videos/play/wwdc2017/701/

約12:10頃Apple=エンジニアは失効トピック全体を詳細に説明します。15:30頃、彼女は通常のOCSPでは追加のAPIの使用が必要になると説明しました。

iOSでのOCSPステープリングのテスト

テストでは、OCSPステープリングをサポートし、失効した証明書を使用するサーバーが必要です。 https://revoked.grc.com (このサーバー障害の回答でこのサーバーが見つかりました: https:// serverfault.com/a/645066

次に、HTML応答をダウンロードしてコンソールに出力しようとする小さなテストプログラムを使用して、iOSから接続を試みます。

上記の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()
}

IOSシミュレーターで上記のルーチンを実行する場合、Wiresharkを使用して、CAによって署名されたタイムスタンプ付きのOCSP応答がTLSハンドシェイクにホチキス止めされているかどうかを確認できます。

nslookup revoked.grc.comサーバーのIPアドレスを取得し、Wiresharkでip.addr==4.79.142.205

スクリーンショットでは、証明書のステータスがrevokedであることがわかります。

wireshark

Xcodesコンソールを確認すると、次の出力が表示されます。

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は、サーバーへの接続試行を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

この証明書がCRL経由で取り消されていることがわかります。

興味深いことに、SafariもChromeもiOSもこの取り消されたステータスを認識しません。MozillaFirefoxのみがエラーメッセージを表示します(ピアの証明書が取り消されました。エラーコード:SEC_ERROR_REVOKED_CERTIFICATE)。

その理由は、証明書がほんの数日前に更新されたため、ブラウザーとオペレーティングシステムのローカルの取り消しリストすべてにまだ届いていないためです。

6