Je ne parviens pas à faire fonctionner les listes de révocation de certificats sur iOS. J'ai créé deux cas de test. J'ai un certificat valide délivré par une autorité de certification. J'ai un autre certificat valide émis par une autorité de certification, mais l'autorité de certification a ajouté ce certificat à sa liste de révocation de certificats.
J'ai ensuite configuré une stratégie de révocation qui permet la vérification de la liste de révocation de certificats et requiert sa réussite.
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
}
Je m'attends à ce que le certificat qui se trouve sur la liste de révocation de certificats ne soit pas approuvé et que le certificat qui est propre soit approuvé.
Compte tenu de la configuration ci-dessus, les deux échouent car non fiables. Si je supprime l'indicateur kSecRevocationRequirePositiveResponse
, les deux réussissent. J'ai essayé toutes les différentes permutations d'utiliser uniquement OSCP ou seulement CRL, et rien ne fonctionne comme je m'y attendais.
Pommes documentation pour SecPolicyCreateRevocation
indique:
Il n'est généralement pas nécessaire de créer vous-même une stratégie de révocation, sauf si vous souhaitez remplacer le comportement système par défaut, par exemple pour forcer une méthode particulière, ou pour désactiver complètement la vérification de la révocation.
Utiliser uniquement le SecPolicyCreateBasicX509
la politique permet aux deux de réussir (lorsque le deuxième certificat doit échouer), est-ce que le comportement par défaut d'Apple ne fait pas du tout la vérification de la liste de révocation de certificats?
J'ai attaché CharlesProxy à mon appareil, et exécuté le code plusieurs fois tout en écoutant tout le trafic réseau, et aucune demande sortante ne va jamais à la liste de révocation de certificats, ce qui explique pourquoi tous échouent lorsque l'indicateur RequirePositiveResponse
est vérifié.
J'ai également essayé de naviguer directement de l'appareil vers la CRL à l'aide d'un URLRequest
, et j'ai pu obtenir les données CRL sur l'appareil sans aucun problème.
La vérification de la liste de révocation de certificats n'est-elle pas prise en charge via la bibliothèque de sécurité Apple? Si tel est le cas, quelqu'un a-t-il déterminé la configuration pour qu'il réponde correctement? Quelles sont les alternatives utilisées par bing pour effectuer la validation de la liste de révocation de certificats, en supposant que les applications mobiles à haute sécurité traitant dans le quartier financier ou d'autres zones sensibles ne permettraient pas cet écart de couverture.
[~ # ~] mise à jour [~ # ~] Pour comparaison, j'ai exécuté certutil -f -urlfetch -verify MYCERT.cer
en utilisant certutil, et j'ai attaché Fiddler à la boîte exécutant la commande. Je reçois les résultats attendus qu'iOS ne me donne pas et je vois une demande sortante vers la CRL via HTTP via fiddler.
J'ai créé une prime pour générer un peu plus d'intérêt à ce sujet. J'espère que quelqu'un a plus de détails sur ce qui est mal fait ci-dessus, ou pourquoi cela ne fonctionne pas sur iOS.
Sur les plates-formes Apple Apple, les clients ne vérifient ni la liste de révocation de certificats (CRL) des autorités de certification, ni n'utilisent OCSP par défaut.
Les plates-formes Apple prennent cependant en charge l'agrafage OCSP et proposent également un mécanisme appelé Revocation Enhancement, qui pourrait en effet conduire à un appel OCSP, voir les détails ci-dessous.
Agrafage OCSP
D'abord une explication de l'agrafage OCSP:
L'agrafage OCSP (Online Certificate Status Protocol) , officiellement appelé TLS Certificate Status Request extension, est une norme pour vérifier l'état de révocation des certificats numériques X.509 . 1 Elle permet au présentateur d'un certificat de supporter le coût des ressources impliquées dans la fourniture du protocole OCSP (Online Certificate Status Protocol) ) réponses en ajoutant ("agrafage") une réponse OCSP horodatée signée par l'autorité de certification à l'établissement de liaison TLS initial, éliminant ainsi la nécessité pour les clients de contacter l'autorité de certification, dans le but d'améliorer à la fois la sécurité et les performances.
voir https://en.wikipedia.org/wiki/OCSP_stapling
Différences entre l'OCSP et l'agrafage OCSP
Si un client se connecte à un serveur dans un flux OCSP traditionnel et récupère le certificat, il vérifie si le certificat reçu a été révoqué en faisant une demande à l'autorité de certification. Cela présente certains inconvénients, par exemple, une connexion réseau supplémentaire est requise, les informations ne sont pas chiffrées et représentent donc un problème de confidentialité des données.
Grâce à l'agrafage OCSP, le serveur demande à l'autorité de certification des informations de révocation signées et les ajoute à la négociation TLS.
Cela signifie également que lorsque vous utilisez l'agrafage OCSP, vous ne voyez pas de demande OCSP depuis iOS vers un serveur CA.
Inconvénients de l'agrafage OCSP
Le serveur auquel vous vous connectez doit prendre en charge l'agrafage OCSP. Cela ne protège pas non plus contre les serveurs malveillants.
Ce sont les principales raisons pour lesquelles Apple fournit une amélioration de la révocation.
Amélioration de la révocation d'Apple
Voici comment ça fonctionne:
Exigence
La seule condition requise pour qu'une application prenne en charge cela est que le certificat de serveur utilisé soit ajouté à un journal de transparence des certificats. Normalement, une autorité de certification le fait déjà, mais vous devez vérifier que le certificat de domaine se trouve dans les journaux de transparence actifs pour les certificats publics, par exemple en utilisant le lien suivant: https://transparencyreport.google.com/https/certificates
WWDC 2017, session 701
Il y a une excellente session WWDC dans laquelle ce sujet et les motivations d'Apple sont expliqués en détail: WWDC 2017, session 701: https://developer.Apple.com/videos/play/wwdc2017/701/
Aux alentours de la minute 12:10 et Apple explique en détail le sujet de la révocation. Vers 15h30, elle explique que l'OCSP normal nécessiterait l'utilisation d'API supplémentaires.
Test d'agrafage OCSP sur iOS
Pour un test, nous avons besoin d'un serveur qui prend en charge l'agrafage OCSP et utilise un certificat révoqué: https://revoked.grc.com (a trouvé ce serveur dans cette réponse par défaut du serveur: https: // serverfault.com/a/645066 )
Ensuite, nous pouvons essayer de se connecter à partir d'iOS avec un petit programme de test qui essaie de télécharger la réponse HTML et de la produire sur la console.
Sur la base des informations de la session WWDC mentionnées ci-dessus, la tentative de connexion doit échouer.
...
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()
}
Si nous exécutons la routine ci-dessus dans le simulateur iOS, nous pouvons utiliser Wireshark pour vérifier si une réponse OCSP horodatée signée par l'autorité de certification est agrafée à la négociation TLS.
Avec nslookup revoked.grc.com
nous obtenons l'adresse IP du serveur et pouvons filtrer dans Wireshark avec ip.addr==4.79.142.205
.
Dans la capture d'écran, on peut voir que le certificat a le statut revoked
.
Donc, en jetant un œil à la console Xcodes, on peut voir la sortie suivante:
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 abandonne la tentative de connexion au serveur avec une erreur TLS.
Test revoked.badssl.com
revoked.badssl.com ne prend pas en charge l'agrafage OCSP.
Si nous examinons les détails du certificat de https://revoked.badssl.com , nous pouvons découvrir:
Si l'on télécharge le fichier .crl (2,5 Mo) et émet un
openssl crl -inform DER -text -in ssca-sha2-g6.crl | grep 0371B58A86F6CE9C3ECB7BF42F9208FC
on peut voir que ce certificat est révoqué via CRL.
Fait intéressant, ni Safari ni Chrome ni iOS ne reconnaissent ce statut révoqué. Seul Mozilla Firefox affiche un message d'erreur ( Le certificat de l'homologue a été révoqué. Code d'erreur: SEC_ERROR_REVOKED_CERTIFICATE) .
La raison pourrait être que le certificat a été renouvelé il y a seulement quelques jours et n'a donc pas encore trouvé sa place dans toutes les listes locales de révocation de navigateurs et de systèmes d'exploitation.