Como fazer com que a CRL e a OSCP Checking funcionem no iOS?

9

Não consigo obter as CRLs funcionando no iOS. Eu criei dois casos de teste. Eu tenho um certificado válido, emitido por uma CA. Tenho outro certificado válido, emitido por uma autoridade de certificação, mas a autoridade de certificação adicionou esse certificado à sua CRL.

Em seguida, configuro uma política de revogação que permite a verificação da CRL e exige que ela seja bem-sucedida.

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
}

Minha expectativa é que o certificado que está na CRL não seja confiável e que o certificado que esteja limpo seja confiável.

Dada a configuração acima, ambos falham como não confiáveis. Se eu remover a kSecRevocationRequirePositiveResponsebandeira, ambos terão êxito. Eu tentei todas as permutações diferentes de usar apenas OSCP ou apenas CRL, e nada funciona da maneira que eu esperaria.

Documentação de maçãs para SecPolicyCreateRevocationestados:

Geralmente, não é necessário criar uma política de revogação, a menos que você deseje substituir o comportamento padrão do sistema, por exemplo, para forçar um método específico ou desativar totalmente a verificação de revogação.

Usar apenas a SecPolicyCreateBasicX509política permite que ambos sejam bem-sucedidos (quando o segundo certificado deve falhar), então o comportamento padrão da Apple é não fazer a verificação da CRL?

Anexei o CharlesProxy ao meu dispositivo e executei o código várias vezes enquanto ouvia todo o tráfego da rede, e nenhuma solicitação de saída jamais foi para a CRL, o que explica por que tudo falha quando o RequirePositiveResponsesinalizador é verificado.

Também tentei navegar diretamente do dispositivo para a CRL usando a URLRequeste consegui obter os dados da CRL no dispositivo sem problemas.

A verificação da CRL não é suportada pela biblioteca de segurança da Apple? Se for, alguém descobriu a configuração para fazer com que ela responda corretamente? Quais são as alternativas usadas para validar a CRL, estou assumindo que aplicativos móveis de alta segurança que lidam no distrito financeiro ou em outras áreas sensíveis não permitiriam essa lacuna de cobertura.

ATUALIZAÇÃO Para comparação, executeicertutil -f -urlfetch -verify MYCERT.cerusando certutil e anexei o Fiddler à caixa que executa o comando. Recebo os resultados esperados que o iOS não está me fornecendo e vejo uma solicitação de saída para a CRL via HTTP através do violinista.

Eu criei uma recompensa para gerar um pouco mais de interesse nisso. Espero que alguém tenha mais detalhes sobre o que está sendo feito de errado acima ou por que isso não funciona no iOS.

Unome
fonte

Respostas:

7

Nas plataformas da Apple, os clientes não verificam a lista de revogação de certificados (CRL) das CAs, nem usam o OCSP por padrão.

As plataformas da Apple, no entanto, oferecem suporte ao grampeamento OCSP e, alternativamente, fornecem um mecanismo que eles chamam de aprimoramento de revogação, que poderia realmente levar a uma chamada OCSP, veja os detalhes abaixo.

Grampeamento OCSP

Primeiro, uma explicação do grampeamento OCSP:

O grampeamento do Online Certificate Status Protocol (OCSP) , formalmente conhecido como extensão TLS Certificate Status Request , é um padrão para verificar o status de revogação dos certificados digitais X.509. 1 Ele permite que o apresentador de um certificado arque com o custo do recurso envolvido no fornecimento de respostas do Protocolo de status do certificado on-line (OCSP) anexando ("grampeando") uma resposta OCSP com carimbo de data e hora assinada pela CA no handshake TLS inicial, eliminando a necessidade para que os clientes entrem em contato com a CA, com o objetivo de melhorar a segurança e o desempenho.

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

Diferenças entre o OCSP e o grampeamento OCSP

Se um cliente se conectar a um servidor em um fluxo OCSP tradicional e recuperar o certificado, ele verificará se o certificado recebido foi revogado fazendo uma solicitação à CA. Isso tem algumas desvantagens, por exemplo, é necessária uma conexão de rede adicional, as informações não são criptografadas e, portanto, representam um problema de privacidade de dados.

Por meio do grampeamento OCSP, o servidor solicita informações de revogação assinadas da CA e as adiciona ao handshake TLS.

Isso também significa que, ao usar o grampeamento OCSP, você não vê uma solicitação OCSP do iOS para um servidor da CA.

Desvantagens do grampeamento OCSP

O servidor ao qual você está se conectando deve suportar o grampeamento OCSP. Isso também não protege contra servidores maliciosos.

Essa é a principal razão pela qual a Apple está fornecendo um aprimoramento de revogação.

Aprimoramento de revogação da Apple

Veja como funciona:

  • registros de transparência de certificados são coletados pela Apple
  • com essas informações, a Apple reúne informações sobre revogações das autoridades de certificação
  • essas informações agregadas são automaticamente disponibilizadas a todos os clientes da Apple regularmente
  • com base nessas informações, quando um aplicativo iOS tenta se conectar ao servidor com um certificado revogado, ele executa uma verificação adicional via OCSP.

Requerimento

O único requisito para um aplicativo suportar isso é que o certificado do servidor usado seja adicionado a um log de transparência do certificado. Normalmente, uma CA já faz isso, mas você deve verificar se o certificado de domínio está nos registros de transparência ativos de certificados públicos, por exemplo, usando o seguinte link: https://transparencyreport.google.com/https/certificates

WWDC 2017, sessão 701

Há uma excelente sessão da WWDC na qual este tópico e os motivos da Apple são explicados em detalhes: WWDC 2017, sessão 701: https://developer.apple.com/videos/play/wwdc2017/701/

Por volta das 12h10, um engenheiro da Apple explica todo o tópico da revogação em detalhes. Por volta das 15:30, ela explica que o OCSP normal exigiria o uso de APIs adicionais.

Teste de grampeamento OCSP no iOS

Para um teste, precisamos de um servidor que suporte o grampeamento OCSP e use um certificado revogado: https://revoked.grc.com (encontrou este servidor nesta resposta de falha do servidor: https://serverfault.com/a/645066 )

Em seguida, podemos tentar conectar-se a partir do iOS com um pequeno programa de teste que tenta baixar a resposta HTML e enviá-la para o console.

Com base nas informações da sessão WWDC mencionada acima, a tentativa de conexão deve falhar.

...
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()
}

Se executarmos a rotina acima no iOS Simulator, poderemos usar o Wireshark para verificar se uma resposta OCSP com registro de data e hora assinada pela CA é grampeada para o handshake TLS.

Com nslookup revoked.grc.comobtemos o endereço IP do servidor e podemos filtrar no Wireshark com ip.addr==4.79.142.205.

Na captura de tela, é possível ver que o certificado possui o status revoked.

wireshark

Então, olhando para o console Xcodes, podemos ver a seguinte saída:

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}

O iOS interrompe a tentativa de conectar-se ao servidor com um erro TLS.

Teste revoked.badssl.com

revoked.badssl.com não suporta grampeamento OCSP.

Se dermos uma olhada nos detalhes do certificado em https://revoked.badssl.com , podemos descobrir:

Se você baixar o arquivo .crl (2,5 MB) e emitir um

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

pode-se ver que este certificado é revogado via CRL.

Curiosamente, nem o Safari, nem o Chrome, nem o iOS reconhecem esse status revogado. Somente o Mozilla Firefox exibe uma mensagem de erro ( o certificado do parceiro foi revogado. Código do erro: SEC_ERROR_REVOKED_CERTIFICATE ).

O motivo pode ser que o certificado foi renovado há apenas alguns dias e, portanto, ainda não foi encontrado em todas as listas locais de revogação de navegadores e sistemas operacionais.

Stephan Schlecht
fonte
Ótima informação aqui. Obrigado pela resposta atenciosa. Como continuei pesquisando esse tópico, vi o mesmo que você, o suporte à CRL está sendo descartado pelos principais navegadores / SOs e o grampeamento OCSP parece ser o novo mecanismo de segurança recomendado. No vídeo da WWDC, o representante da Apple declara: "Infelizmente, nossas plataformas NÃO verificam a revogação por padrão atualmente". O que eu encontrei através de minhas experiências é que não só é não é suportado por padrão, mas não é suportado em todos (mesmo se você forçar as configurações no) @Stephan Schlecht
Unome