Faire la demande HTTPS dans iOS 9 avec un certificat auto-signé
Je veux faire une requête HTTPS serveur personnalisé avec certificat auto-signé. Je suis en utilisant NSURLConnection de classe et de traitement des défis de l'authentification, mais toujours un message d'erreur dans une console:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
alors de "méthode de connexion:didFailWithError:" est appelée avec l'erreur suivante:
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x150094100>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=<CFArray 0x1500ddd90 [0x19f6dab68]>{type = immutable, count = 1, values = (
0 : <cert(0x14e6fb370) s: (server certificate name) i: (custom CA name)>
)}, NSUnderlyingError=0x1504ae170 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=https://217.92.80.156:9090/(method name and parameters), NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, kCFStreamPropertySSLPeerCertificates=<CFArray 0x1500ddd90 [0x19f6dab68]>{type = immutable, count = 1, values = (
0 : <cert(0x14e6fb370) s: (server certificate name) i: (custom CA name)>
)}, _kCFStreamPropertySSLClientCertificateState=2, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x150094100>, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., _kCFStreamPropertySSLClientCertificates=<CFArray 0x14e5ee8e0 [0x19f6dab68]>{type = mutable-small, count = 2, values = (
0 : <SecIdentityRef: 0x15012cd40>
1 : <cert(0x15014aa70) s: (client certificate name) i: (custom CA name)>
)}, _kCFStreamErrorDomainKey=3, NSErrorFailingURLKey=https://217.92.80.156:9090/(method name and parameters), _kCFStreamErrorCodeKey=-9802}}, NSErrorClientCertificateChainKey=<CFArray 0x14e5ee8e0 [0x19f6dab68]>{type = mutable-small, count = 2, values = (
0 : <SecIdentityRef: 0x15012cd40>
1 : <cert(0x15014aa70) s: (client certificate name) i: (custom CA name)>
)}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://217.92.80.156:9090/(method name and parameters), NSErrorFailingURLStringKey=https://217.92.80.156:9090/(method name and parameters), NSErrorClientCertificateStateKey=2}
Application reçoit deux défis de l'authentification (NSURLAuthenticationMethodClientCertificate et NSURLAuthenticationMethodServerTrust) et les traite d'une manière suivante:
- (void) connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if(challenge.proposedCredential && !challenge.error)
{
[challenge.sender useCredential:challenge.proposedCredential forAuthenticationChallenge:challenge];
return;
}
NSString *strAuthenticationMethod = challenge.protectionSpace.authenticationMethod;
NSLog(@"authentication method: %@", strAuthenticationMethod);
NSURLCredential *credential = nil;
if([strAuthenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
{
//get identity and certificate from p.12
NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];
NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"password" forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data,(__bridge CFDictionaryRef)optionsDictionary, &items);
SecIdentityRef identity = NULL;
SecCertificateRef certificate = NULL;
if(securityError == errSecSuccess)
{
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
identity = (SecIdentityRef)CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
CFArrayRef array = (CFArrayRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemCertChain);
certificate = (SecCertificateRef)CFArrayGetValueAtIndex(array, 0);
}
credential = [NSURLCredential credentialWithIdentity:identity certificates:[NSArray arrayWithObject:(__bridge id)(certificate)] persistence:NSURLCredentialPersistenceNone];
CFRelease(items);
}
else if([strAuthenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
int trustCertificateCount = (int)SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);
NSMutableArray *trustCertificates = [[NSMutableArray alloc] initWithCapacity:trustCertificateCount];
for(int i = 0; i < trustCertificateCount; i ++)
{
SecCertificateRef trustCertificate = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
[trustCertificates addObject:(__bridge id) trustCertificate];
}
SecPolicyRef policyRef = NULL;
policyRef = SecPolicyCreateSSL(YES, (__bridge CFStringRef) challenge.protectionSpace.host);
SecTrustRef trustRef = NULL;
if(policyRef)
{
SecTrustCreateWithCertificates((__bridge CFArrayRef) trustCertificates, policyRef, &trustRef);
CFRelease(policyRef);
}
if(trustRef)
{
// SecTrustSetAnchorCertificates(trustRef, (__bridge CFArrayRef) [NSArray array]);
// SecTrustSetAnchorCertificatesOnly(trustRef, NO);
SecTrustResultType result;
OSStatus trustEvalStatus = SecTrustEvaluate(trustRef, &result);
if(trustEvalStatus == errSecSuccess)
{
//just temporary attempt to make it working.
//i hope, there is no such problem, when we have final working version of certificates.
if(result == kSecTrustResultRecoverableTrustFailure)
{
CFDataRef errDataRef = SecTrustCopyExceptions(trustRef);
SecTrustSetExceptions(trustRef, errDataRef);
SecTrustEvaluate(trustRef, &result);
}
if(result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)
credential = [NSURLCredential credentialForTrust:trustRef];
}
CFRelease(trustRef);
}
}
else
{
DDLogWarn(@"Unexpected authentication method. Cancelling authentication ...");
[challenge.sender cancelAuthenticationChallenge:challenge];
}
if(credential)
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
else
[challenge.sender cancelAuthenticationChallenge:challenge];
}
Dans CFNetwork journal de diagnostic, je peux voir que la poignée de main de la procédure est sur le point d'être lancé. Au moins une application envoie des "ClientHello" message, le serveur envoie son "ServerHello" message et nécessite pour l'authentification. Et ici application tente d'envoyer la réponse d'authentification, mais reçoit immédiatement l'erreur. (En même temps, dans les journaux du serveur, je ne vois pas de messages à propos de la poignée de main à tous). Voici la première partie du journal de diagnostic:
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:49] 10:51:49.185 {
Authentication Challenge
Loader: <CFURLRequest 0x1501931c0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Challenge: challenge space https://217.92.80.156:9090/, ServerTrustEvaluationRequested (Hash f9810ad8165b3620)
} [3:49]
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:50] 10:51:49.189 {
Use Credential
Loader: <CFURLRequest 0x1501931c0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Credential: Name: server, Persistence: session
} [3:50]
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:51] 10:51:49.190 {
touchConnection
Loader: <CFURLRequest 0x1501931c0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Timeout Interval: 60.000 seconds
} [3:51]
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:52] 10:51:49.192 {
Response Error
Request: <CFURLRequest 0x14e5d02a0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Error: Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFNetworkCFStreamSSLErrorOriginalValue=-9802, kCFStreamPropertySSLPeerCertificates=<CFArray 0x1500ddd90 [0x19f6dab68]>{type = immutable, count = 1, values = (
0 : <cert(0x14e6fb370) s: (server certificate name) i: (custom CA name)>
)}, _kCFStreamPropertySSLClientCertificateState=2, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x150094100>, _kCFStreamPropertySSLClientCertificates=<CFArray 0x14e5ee8e0 [0x19f6dab68]>{type = mutable-small, count = 2, values = (
0 : <SecIdentityRef: 0x15012cd40>
1 : <cert(0x15014aa70) s: (client certificate name) i: (custom CA name)>
)}, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802}
} [3:52]
Notre retour en fin d'instance peut être installé sur le côté client, donc je ne peux pas définir un domaine d'exception en Info.fichier plist. Aussi application peut demander serveur par son adresse IP en IPv4, mais pas par nom de domaine (comme dans mon exemple).
Ce que j'ai essayé:
- utilisé NSURLSession au lieu de NSURLConnection, mais sans succès;
- vérifié Apple ATS exigences pour la mise en œuvre de serveur ici (développeur back-end est sûr que sa mise en œuvre répond à tous d'entre eux);
- joué avec réglage d'ancrage des certificats de confiance de validation en conformité avec les différentes résolu les problèmes de stackoverflow et Apple developer forums;
- attention particulièrement à similaire de poste et de ses solution à des forums de développeurs;
Je suis en essais https demande sur l'iPad Air 2 avec iOS 9 les Semences génétiquement modifiées (Build 13A340) et xCode 7 les Semences génétiquement modifiées (Build 7A218). Remarque importante: cette fonctionnalité fonctionne bien avec iOS 8. En tenant compte que je peut supposer, que le problème est dans notre serveur, mais notre développeur back-end m'a assuré que tout est bien.
Maintenant, je suis à court d'idées. Je vous serais reconnaissant si quelqu'un peut me donner un indice, ou au moins de suggérer quelques autres de diagnostic, ce qui serait révélateur d'erreur particulier, plus spécifique que "alerte fatale".
Grâce.
EDIT 1: SecTrustEvaluate retourne toujours kSecTrustResultRecoverableTrustFailure, c'est pourquoi j'ai dû trouver une sorte de solution de contournement.
Pas encore, je dois travailler sur d'autres tâches prioritaires, pour plusieurs raisons. Je vais vous contacter si j'ai une solution.
C'est un iOS PITA erreur de protocole. (P. I. T. A. est synonyme de douleur dans l' ....)
OriginalL'auteur alfared | 2015-09-15
Vous devez vous connecter pour publier un commentaire.
Avez-vous utilisé nscurl pour diagnostiquer le problème de connexion? Si vous avez un Mac exécutant OS X v10.11 vous pouvez exécuter quelque chose comme ceci:
Alternativement, si vous n'avez pas 10.11, vous pouvez télécharger l'exemple de code ici: https://developer.apple.com/library/mac/samplecode/SC1236/ et construire avec XCode et l'exécuter comme ceci (modifier le chemin d'accès en fonction de votre machine):
(Pour trouver le chemin d'accès complet pour le haut, après que vous avez construit, ouvrez le groupe de Produits dans votre Projet de navigation, cliquez-droit sur TLSTool, et "Afficher dans le Finder".)
Vous déjà lié à Apple note technique sur ce sujet, https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/ mais vous n'avez pas dit si vous avez exécuté nscurl ou pas.
OriginalL'auteur DFedor
Selon cette: https://forums.developer.apple.com/message/36842#36842
La meilleure approche pour résoudre HTTP échec de chargement (kCFStreamErrorDomainSSL, -9802) est de définir une exception dans l'info.fichier plist comme suit:
Le point important est que ce n'est pas moins sûr que iOS8, tout simplement pas aussi sûr que plein ATS pris en charge par iOS9.
Merci beaucoup, mais: 1. Qui permet d'ordinaire de téléchargement HTTP, et j'ai besoin de HTTPS (pour des raisons de sécurité). 2. Comme je l'ai écrit, je ne peux pas ne pas définir de n'importe quel domaine dans la liste d'exception, parce que nos clients ont souvent installer back-end sur leurs propres serveurs. Par exemple, il peut être "server.company1.com" et "server.company2.com". Votre réponse suppose que je dois re-construire application et ajouter une nouvelle exception du domaine "server.company3.com" après nous vendre de l'application de la troisième compagnie.
Vous pouvez être en mesure d'utiliser une combinaison de l'information à partir de la documentation d'Apple sur ATS <developer.apple.com/library/prerelease/ios/technotes/...> ainsi que cet article de blog sur les certificats auto-signés #5, en particulier, <blog.httpwatch.com/2013/12/12/...> pour résoudre votre problème (vous aurez probablement besoin d'itérer à quelques reprises jusqu'à ce que vous obteniez la bonne).
vos deux liens sont morts
Vous êtes en droit (un supplément de
>
ai capturé dans l'URL), et je n'arrive pas à modifier le commentaire, donc, je vais ajouter ici de nouveau. developer.apple.com/library/prerelease/ios/technotes/... et blog.httpwatch.com/2013/12/12/...OriginalL'auteur spirographer
Je viens de rencontrer le même problème avec ur.Maintenant, je la corrige.C'est parce que le tls version et le certificat signe.Comme la pomme de document de dire ci-dessous
apple document
Donc je fais cette chose
info.plist réglage
et il fonctionne
OriginalL'auteur zms
Ce problème a été résolu il y a quelques temps. Il s'est avéré être non valide certificat auto-signé. Il n'a pas satisfait à toutes les exigences d'Apple. Malheureusement, je ne sais pas, ce qu'il était exactement.
OriginalL'auteur alfared