J'ai le code simple suivant pour me connecter à une page Web SSL
NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];
Sauf que cela donne une erreur si le cert est un auto-signé Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".
Y at-il un moyen de le configurer pour accepter les connexions de toute façon (comme dans un navigateur, vous pouvez appuyer sur Accepter) ou un moyen de le contourner?
Il existe une API prise en charge pour accomplir cela! Ajoutez quelque chose comme ceci à votre délégué NSURLConnection
:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
if ([trustedHosts containsObject:challenge.protectionSpace.Host])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
Notez que connection:didReceiveAuthenticationChallenge:
peut envoyer son message à challenge.sender (beaucoup) plus tard, après avoir présenté une boîte de dialogue à l'utilisateur si nécessaire, etc.
Si vous ne souhaitez pas (ou ne pouvez pas) utiliser des API privées, il existe une bibliothèque open source (licence BSD) appelée ASIHTTPRequest qui fournit une enveloppe autour du CFNetwork APIs
de niveau inférieur. Ils ont récemment introduit la possibilité d’autoriser HTTPS connections
à utiliser des certificats auto-signés ou non approuvés avec l’API -setValidatesSecureCertificate:
. Si vous ne souhaitez pas extraire toute la bibliothèque, vous pouvez utiliser le source comme référence pour implémenter vous-même la même fonctionnalité.
Idéalement, il ne devrait exister que deux scénarios pour le moment où une application iOS devrait accepter un certificat non approuvé.
Scénario A: Vous êtes connecté à un environnement de test utilisant un certificat auto-signé.
Scénario B: Vous utilisez un proxy HTTPS
à l'aide d'un MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.
. Les mandataires renverront un certificat signé par une autorité de certification auto-signée afin que le proxy puisse capturer le trafic HTTPS
.
Les hôtes de production ne doivent jamais utiliser de certificats non approuvés pour raisons évidentes .
Si le simulateur iOS doit accepter un certificat non approuvé à des fins de test, il est vivement recommandé de ne pas modifier la logique de l'application afin de désactiver la validation de certificat intégrée fournie par les API NSURLConnection
. Si l'application est rendue publique sans supprimer cette logique, elle sera exposée aux attaques de type "man-in-the-middle".
La méthode recommandée pour accepter des certificats non approuvés à des fins de test consiste à importer le certificat de l'autorité de certification (AC) ayant signé le certificat sur votre appareil iOS Simulator ou votre périphérique iOS. J'ai écrit un article de blog rapide qui explique comment procéder, à l'aide d'un simulateur iOS:
acceptation de certificats non fiables à l'aide du simulateur ios
NSURLRequest
a une méthode privée appelée setAllowsAnyHTTPSCertificate:forHost:
, qui fera exactement ce que vous voulez. Vous pouvez définir la méthode allowsAnyHTTPSCertificateForHost:
sur NSURLRequest
via une catégorie et la définir pour renvoyer YES
pour l'hôte à remplacer.
Pour compléter la réponse acceptée, pour une bien meilleure sécurité, vous pouvez ajouter votre certificat de serveur ou votre propre certificat d’autorité de certification racine au trousseau ( https://stackoverflow.com/a/9941559/1432048 ). ne faites pas que NSURLConnection authentifie votre serveur auto-signé automatiquement. Vous devez toujours ajouter le code ci-dessous à votre délégué NSURLConnection. Il est copié à partir du code exemple Apple AdvancedURLConnections et vous devez ajouter deux fichiers (Credentials.h, Credentials.m) à partir du code exemple Apple dans vos projets.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// if ([trustedHosts containsObject:challenge.protectionSpace.Host])
OSStatus err;
NSURLProtectionSpace * protectionSpace;
SecTrustRef trust;
SecTrustResultType trustResult;
BOOL trusted;
protectionSpace = [challenge protectionSpace];
assert(protectionSpace != nil);
trust = [protectionSpace serverTrust];
assert(trust != NULL);
err = SecTrustEvaluate(trust, &trustResult);
trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
// If that fails, apply our certificates as anchors and see if that helps.
//
// It's perfectly acceptable to apply all of our certificates to the SecTrust
// object, and let the SecTrust object sort out the mess. Of course, this assumes
// that the user trusts all certificates equally in all situations, which is implicit
// in our user interface; you could provide a more sophisticated user interface
// to allow the user to trust certain certificates for certain sites and so on).
if ( ! trusted ) {
err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates);
if (err == noErr) {
err = SecTrustEvaluate(trust, &trustResult);
}
trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
}
if(trusted)
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
Je ne peux prendre aucun crédit pour ceci, mais celui-ci que j'ai trouvé a vraiment bien fonctionné pour mes besoins. shouldAllowSelfSignedCert
est ma variable BOOL
. Ajoutez simplement à votre délégué NSURLConnection
et vous devriez être prêt pour un contournement rapide connexion par connexion.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space {
if([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if(shouldAllowSelfSignedCert) {
return YES; // Self-signed cert will be accepted
} else {
return NO; // Self-signed cert will be rejected
}
// Note: it doesn't seem to matter what you return for a proper SSL cert
// only self-signed certs
}
// If no other authentication is required, return NO for everything else
// Otherwise maybe YES for NSURLAuthenticationMethodDefault and etc.
return NO;
}
Dans iOS 9, les connexions SSL échoueront pour tous les certificats non valides ou auto-signés. Il s'agit du comportement par défaut de la nouvelle fonctionnalité App Transport Security dans iOS 9.0 ou version ultérieure, et sous OS X 10.11 ou ultérieure.
Vous pouvez remplacer ce comportement dans le Info.plist
en définissant NSAllowsArbitraryLoads
sur YES
dans le dictionnaire NSAppTransportSecurity
. Cependant, je vous recommande de remplacer ce paramètre à des fins de test uniquement.
Pour plus d'informations, voir App Transport Technote ici .
La solution de contournement de catégorie publiée par Nathan de Vries réussira les vérifications de l'API privée de l'AppStore et est utile dans les cas où vous n'avez pas le contrôle de l'objet NSUrlConnection
.. .. Un exemple est NSXMLParser
qui s'ouvre l'URL que vous fournissez, mais n'expose pas les options NSURLRequest
ou NSURLConnection
.
Dans iOS 4, la solution de contournement semble toujours fonctionner, mais uniquement sur le périphérique, le simulateur n'appelle plus la méthode allowsAnyHTTPSCertificateForHost:
.
Vous devez utiliser NSURLConnectionDelegate
pour autoriser les connexions HTTPS et il y a de nouveaux rappels avec iOS8.
Obsolète:
connection:canAuthenticateAgainstProtectionSpace:
connection:didCancelAuthenticationChallenge:
connection:didReceiveAuthenticationChallenge:
Au lieu de cela, vous devez déclarer:
connectionShouldUseCredentialStorage:
- Envoyé pour déterminer si le chargeur d'URL doit utiliser le stockage des informations d'identification pour authentifier la connexion.
connection:willSendRequestForAuthenticationChallenge:
- Indique au délégué que la connexion enverra une demande de défi d'authentification.
Avec willSendRequestForAuthenticationChallenge
, vous pouvez utiliser challenge
comme vous l'avez fait avec les méthodes obsolètes, par exemple:
// Trusting and not trusting connection to Host: Self-signed certificate
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
Si vous souhaitez continuer à utiliser sendSynchronousRequest, je travaille dans cette solution:
FailCertificateDelegate *fcd=[[FailCertificateDelegate alloc] init];
NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO];
[c setDelegateQueue:[[NSOperationQueue alloc] init]];
[c start];
NSData *d=[fcd getData];
vous pouvez le voir ici: Connexion synchrone SSL Objective-C
J'ai posté un code Gist (basé sur le travail de quelqu'un d'autre que je note) qui vous permet de vous authentifier correctement avec un certificat auto-généré (et comment obtenir un certificat gratuit - voir les commentaires au bas de Cocoanetics )
Mon code est ici github
Avec AFNetworking J'ai utilisé avec succès le service Web https avec le code ci-dessous,
NSString *aStrServerUrl = WS_URL;
// Initialize AFHTTPRequestOperationManager...
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
manager.securityPolicy.allowInvalidCertificates = YES;
[manager POST:aStrServerUrl parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
successBlock(operation, responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
errorBlock(operation, error);
}];
Vous pouvez utiliser ce code
-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust)
{
[[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge];
}
}
Utilisez -connection:willSendRequestForAuthenticationChallenge:
au lieu de ces méthodes obsolètes
Obsolète:
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge