我正在尝试使用客户端证书身份验证来访问安全的网站.我使用的代码在iOS 6.1中工作正常,但在使用iOS 7时服务器返回403.7错误失败.
我使用连接:willSendRequestForAuthenticationChallenge处理程序来检查身份验证方法并提供客户端证书.
我的代码是:
- (void)connection: (NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { NSLog(@"Trust Challenge"); SecTrustResultType trustResultType; Osstatus err = SecTrustEvaluate(challenge.protectionSpace.serverTrust,&trustResultType); NSLog(@"SecTrustResult %u %d",trustResultType,(int)err); if (trustResultType == kSecTrustResultProceed || trustResultType == kSecTrustResultConfirm || trustResultType == kSecTrustResultUnspecified) { [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; } else{ [challenge.sender cancelAuthenticationChallenge:challenge]; } } else { Nsstring *path = [[NSBundle mainBundle]pathForResource:@"mycert" ofType:@"pfx"]; NSData *p12data = [NSData dataWithContentsOfFile:path]; CFDataRef inP12data = (__bridge CFDataRef)p12data; SecIdentityRef myIdentity; SecTrustRef myTrust; extractIdentityAndTrust(inP12data,&myIdentity,&myTrust); assert(myIdentity != nil); assert(myTrust != nil); long count = SecTrustGetCertificateCount(myTrust); NSMutableArray* myCertificates = nil; if(count > 1) { myCertificates = [NSMutableArray arrayWithCapacity:count]; for(int i = 1; i < count; ++i) { [myCertificates addobject:(__bridge id)SecTrustGetCertificateAtIndex(myTrust,i)]; } } NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:myCertificates persistence:NSURLCredentialPersistenceNone]; assert(credential != nil); NSLog(@"User: %@,certificates %@ identity:%@",[credential user],[credential certificates],[credential identity]); [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; } }
Osstatus extractIdentityAndTrust(CFDataRef inP12data,SecIdentityRef *identity,SecTrustRef *trust) { Osstatus securityError = errSecSuccess; CFStringRef password = CFSTR("password"); const void *keys[] = { kSecImportExportPassphrase }; const void *values[] = { password }; CFDictionaryRef options = CFDictionaryCreate(NULL,keys,values,1,NULL,NULL); CFArrayRef items = CFArrayCreate(NULL,NULL); securityError = SecPKCS12Import(inP12data,options,&items); if (securityError == 0) { CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items,0); const void *tempIdentity = NULL; tempIdentity = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemIdentity); *identity = (SecIdentityRef)tempIdentity; const void *tempTrust = NULL; tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust); *trust = (SecTrustRef)tempTrust; CFIndex count = CFArrayGetCount(items); NSLog(@"Certificates found: %ld",count); } if (options) { CFRelease(options); } return securityError; }
mycert.pfx文件包含中间证书以及客户端证书.
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"Error: %@",[error userInfo]); }
所以看起来证书协商在某种程度上成功了.
我的问题类似于SSL – behaves differently in iOS7?,但我使用的是带有IIS 7.5的Windows Server 2008 R2.已在服务器上启用TLS 1.1和TLS 1.2协议.
WireShark跟踪显示使用iOS 7时TLS握手期间的证书框架为空.使用iOS 6.1时会发送和验证证书.
我可以使用Safari访问iOS 7中的网站.
任何帮助是极大的赞赏.
解决方法
我在Apple Developer支持的帮助下找到了解决方案.该解决方案涉及创建自定义NSURLProtocol.我在
https://developer.apple.com/library/ios/#samplecode/CustomHTTPProtocol/使用了Apple示例代码.示例代码显示了如何覆盖HTTPS服务器信任评估,因此需要对其进行修改以使用客户端证书身份验证.
我修改了AppDelegate didRecieveAuthenticationChallenge函数.
- (void)customHTTPProtocol:(CustomHTTPProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge // A CustomHTTPProtocol delegate callback,called when the protocol has an authenticate // challenge that the delegate accepts via - customHTTPProtocol:canAuthenticateAgainstProtectionSpace:. // In this specific case it's only called to handle server trust authentication challenges. // It evaluates the trust based on both the global set of trusted anchors and the list of trusted // anchors returned by the CredentialsManager. { Osstatus err; NSURLCredential * credential; assert(protocol != nil); assert(challenge != nil); credential = nil; // Handle ServerTrust and Client Certificate challenges Nsstring *authenticationMethod = [[challenge protectionSpace] authenticationMethod]; if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { NSLog(@"Trust Challange"); SecTrustResultType trustResultType; err = SecTrustEvaluate(challenge.protectionSpace.serverTrust,(int)err); if (trustResultType == kSecTrustResultProceed || trustResultType == kSecTrustResultUnspecified) { credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; assert(credential != nil); } } else { Nsstring *path = [[NSBundle mainBundle]pathForResource:@"mycert" ofType:@"pfx"]; NSData *p12data = [NSData dataWithContentsOfFile:path]; SecIdentityRef identity = NULL; SecCertificateRef certificate = NULL; [Util identity:&identity andCertificate:&certificate fromPKCS12Data:p12data withPassphrase:@"asia1215"]; assert(identity != NULL); NSArray *certArray = [NSArray arrayWithObject:(__bridge id)certificate]; credential = [NSURLCredential credentialWithIdentity:identity certificates:certArray persistence:NSURLCredentialPersistencePermanent]; } [protocol resolveAuthenticationChallenge:challenge withCredential:credential]; }