web-dev-qa-db-fra.com

Comment utiliser NSURLConnection pour se connecter à SSL pour un certificat non approuvé?

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?

292
erotsppa

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.

414
Gordon Henriksen

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é.

35
Nathan de Vries

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

33
user890103

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.

12
Nathan de Vries

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];
}
11
xiang

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;
}
10
Ryna

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.

 enter image description here

Pour plus d'informations, voir App Transport Technote ici .

10
johnnieb

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:.

6
Alex Suzuki

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];
5
ricardopereira

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

2
jgorozco

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

2
David H

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);
}];
0
cjd

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
0
Vaibhav Sharma