写在前面:
3.这篇博客侧重于 iOS客户端相关内容的描述,OC语言,基于AFNetwork3.0版本。
本篇内容
2.ATS设置
1.服务器公钥和p12文件。服务器公钥网上很多代码是 .cer格式的,但是在AF3.0版本中,支持的是.der格式,这个坑请注意了。
对公钥证书data的描述 这个方法添加的是公钥证书的data数据p12证书 对应的是服务器上的 .pfx证书(应该是,不太确定),pfx格式的可以转换成p12证书,它里面包括了公钥和私钥,客户端用p12证书应该是在做客户端的验证。
这是我自己配置的tomcat配置,p12证书对应的是 keystoreFile所对应的证书。注意:在添加读取证书的时候,Xcode7有个bug 但不是必现。当证书拖到项目中时,路径读不出来,找不到这个文件,但是Bundle Resource中却偏偏存在,当时我一直认为是证书的问题,纠结了很久。
这里添加证书,一般可以解决无法读取路径的问题 这个证书路径容易读出来是 nil2.ATS设置
因此还是要设置一下。
这里的域名下,TLS传输协议需要服务器端的注意一下,iOS支持的是1.2版本开始。但是很多都是从1.0开始的。因此我们最好也配上去。 与正常的AFN请求http请求来说,添加一这一个设置```
-(AFSecurityPolicy*) getCustomHttpsPolicy:(AFHTTPSessionManager*)manager{
NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"custom" ofType:@"der"];
NSData *certData = [NSData dataWithContentsOfFile:certFilePath];
NSSet *certSet = [NSSet setWithObject:certData];
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certSet];
policy.allowInvalidCertificates = YES;
policy.validatesDomainName = NO;//是否校验证书上域名与请求域名一致
[manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
NSLog(@"setSessionDidBecomeInvalidBlock");
}];
__weak typeof(manager)weakManger = manager;
__weak typeof(self)weakSelf = self;
//客户端请求验证 重写 setSessionDidReceiveAuthenticationChallengeBlock 方法
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *credential =nil;
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if([weakManger.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if(credential) {
disposition =NSURLSessionAuthChallengeUseCredential;
} else {
disposition =NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
// client authentication
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
if ([[weakSelf class]extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
{
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
const void*certs[] = {certificate};
CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
*_credential = credential;
return disposition;
}];
return policy;
}
```
```
+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
OSStatus securityError = errSecSuccess;
//client certificate password
NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"111111"
forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
if(securityError == 0) {
CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
const void*tempIdentity =NULL;
tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
const void*tempTrust =NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
} else {
NSLog(@"Failedwith error code %d",(int)securityError);
return NO;
}
return YES;
}
```
这里需要注意一下,这个需要设置主要代码:
```
#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *tmp = [request.URL absoluteString];
NSLog(@"request url :%@",tmp);
//开启同步的请求去双向认证
if (!_Authenticated) {
originRequest = request;
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
[conn start];
[webView stopLoading];
return false;
}
}
return YES;
}
```
```
#pragma NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSURLCredential * credential;
assert(challenge != nil);
credential = nil;
NSLog(@"----received challenge----");
NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];
if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSLog(@"----server verify client----");
NSString *host = challenge.protectionSpace.host;
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
BOOL validDomain = false;
NSMutableArray *polices = [NSMutableArray array];
if (validDomain) {
[polices addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)host)];
}else{
[polices addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)polices);
//pin mode for certificate
NSString *path = [[NSBundle mainBundle] pathForResource:@"custom" ofType:@"der"];
NSData *certData = [NSData dataWithContentsOfFile:path];
NSMutableArray *pinnedCerts = [NSMutableArray arrayWithObjects:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData), nil];
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCerts);
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
NSLog(@"----client verify server----");
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:p12]) {
NSLog(@"client.p12 file not exist!");
}else{
NSData *pkcs12Data = [NSData dataWithContentsOfFile:p12];
if ([[self class] extractIdentity:&identity andTrust:&trust fromPKCS12Data:pkcs12Data]) {
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
const void *certs[] = {certificate};
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray *)certArray persistence:NSURLCredentialPersistencePermanent];
}
}
}
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
```
```
+ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
OSStatus securityErr = errSecSuccess;
//client certificate password
NSDictionary *optionsDic = [NSDictionary dictionaryWithObject:@"111111" forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityErr = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data, (__bridge CFDictionaryRef)optionsDic, &items);
if (securityErr == errSecSuccess) {
CFDictionaryRef mineIdentAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tmpIdentity = NULL;
tmpIdentity = CFDictionaryGetValue(mineIdentAndTrust, kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tmpIdentity;
const void *tmpTrust = NULL;
tmpTrust = CFDictionaryGetValue(mineIdentAndTrust, kSecImportItemTrust);
*outTrust = (SecTrustRef)tmpTrust;
}else{
return false;
}
return true;
}
```
```
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)pResponse {
_Authenticated = YES;
//webview 重新加载请求。
[localWebView loadRequest:originRequest];
[connection cancel];
}
```
```
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
```
这三个方法去处理的,方式是:将收到的data拼成一个全局的变量,在连接完成时将data转换成字符串,用加载本地html的方式进行加载。
这个方法不可行,在加载非本地html的时候。
js、css、图片的引入都是根据相对路径的因此,在收到pResponse的时候,直接断掉connection,重新加载该页面就ok。这个时候,客户端和服务器都是相互信任的,ssl通道已经建立起来,大家可以愉快的进行操作了。