1: AFNetworking 进行HTTPS认证
// // FFBaseNetwork.h // Temp // // Created by jisa on 2019/7/30. // Copyright © 2019 jff. All rights reserved. // #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface FFBaseNetwork : NSObject @end NS_ASSUME_NONNULL_END
// // FFBaseNetwork.m // Temp // // Created by jisa on 2019/7/30. // Copyright © 2019 jff. All rights reserved. // #import "FFBaseNetwork.h" #import <AFNetworking/AFNetworking.h> @interface FFBaseNetwork () @end @implementation FFBaseNetwork /// 进行HTTPS验证的核心代码 - (void)FF_httpsValide:(AFHTTPSessionManager *)manager { /* *SSLPinning 证书绑定 客户端保存服务器端的证书,在建立https连接时比较服务器返回的证书是否和客户端的证书一致 * * *AFSSLPinningModeNone 像浏览器一样在系统的信任机构列表里验证服务器端返回的证书 * *AFSSLPinningModePublicKey 证书绑定,只验证公钥,不验证证书的有效期 * *AFSSLPinningModeCertificate 证书绑定,第一步验证域名,有效期等信息。等而不验证证书信息 */ AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; /// 是否允许无效的证书。 自制的https证书,在Apple中属于无效证书(不信任的证书) policy.allowInvalidCertificates = YES; /// 是否验证域名。默认为YES。如果使用的域名与证书域名不一致要设置为NO。 policy.validatesDomainName = NO; NSString *path = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"]; NSData *data = [NSData dataWithContentsOfFile:path]; /// policy.pinnedCertificates = [[NSSet alloc] initWithObjects:data, nil]; manager.securityPolicy = policy; /// 质疑block __weak typeof(self) weakSelf = self; [manager setTaskDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLSessionTask * _Nonnull task, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing _Nullable * _Nullable credential) { __strong typeof(weakSelf) strongSelf = weakSelf; __autoreleasing NSURLCredential *_credential = NULL; NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { /// 判断是否信任服务器证书 /// 服务器证书是否可以被信任 if ([policy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { _credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; if (_credential) { disposition = NSURLSessionAuthChallengeUseCredential; } }else { disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; } }else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) { /// 客户端证书验证 /// 双向认证才会走,服务器需要验证客户端的p12证书 NSString *path = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]; NSData *p12Data = [NSData dataWithContentsOfFile:path options:0 error:NULL]; SecIdentityRef identity = NULL; SecTrustRef trust = NULL; if ([strongSelf FF_extractIdentity:&identity andTrust:&trust fromData:p12Data]) { SecCertificateRef certificateRef = NULL; SecIdentityCopyCertificate(identity, &certificateRef); const void *certs[] = {certificateRef}; CFArrayRef arrayRef = CFArrayCreate(NULL, certs, 1, NULL); _credential = [[NSURLCredential alloc] initWithIdentity:identity certificates:(__bridge NSArray *)arrayRef persistence:NSURLCredentialPersistencePermanent]; disposition = NSURLSessionAuthChallengeUseCredential; } } *credential = _credential; return disposition; }]; } /// 从客户端证书中获取 SecIdentity 和 SecTrustRef。 - (BOOL)FF_extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef *)outTrust fromData:(NSData *)data { NSDictionary *dic = @{(__bridge NSString *)kSecImportExportPassphrase : @"123456"}; CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); OSStatus status = SecPKCS12Import((__bridge CFDataRef)data, (__bridge CFDictionaryRef)dic, &items); if (status == errSecSuccess) { CFDictionaryRef identityAndTrust = CFArrayGetValueAtIndex(items, 0); const void *tempIdentity = NULL; tempIdentity = CFDictionaryGetValue(identityAndTrust, kSecImportItemIdentity); *outIdentity = (SecIdentityRef)tempIdentity; const void *tempTrust = CFDictionaryGetValue(identityAndTrust, kSecImportItemTrust); *outTrust = (SecTrustRef)tempTrust; return YES; }else { return NO; } } - (AFHTTPSessionManager *)manager { AFHTTPSessionManager *manager = [[AFHTTPSessionManager manager] initWithBaseURL:NULL]; manager.requestSerializer = [AFJSONRequestSerializer serializer]; manager.responseSerializer = [AFJSONResponseSerializer serializer]; manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; manager.requestSerializer.timeoutInterval = 30; manager.responseSerializer.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"json/text", nil]; return manager; } @end
2: SDWebImage 中使用HTTPS如果是双向认证的话,设置客户端证书如下
如果是自制的HTTPS证书 在 SDWebImageDownloaderOptions 中要包括 SDWebImageDownloaderAllowInvalidSSLCertificates。允许无效的证书
// // SDWebImageDownloader+AFNHTTPS.m // TongCheng // // Created by jisa on 2019/7/30. // Copyright © 2019 jisa. All rights reserved. // #import "SDWebImageDownloader+AFNHTTPS.h" @implementation SDWebImageDownloader (AFNHTTPS) + (void)load { [SDWebImageDownloader sharedDownloader].urlCredential = [self myURLCredential]; } + (NSURLCredential *)myURLCredential { SecIdentityRef identity = NULL; NSString *p12Path = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]; NSData *data = [NSData dataWithContentsOfFile:p12Path]; [self extractIdentity:&identity fromPKCS12Data:data]; SecCertificateRef certificate = NULL; SecIdentityCopyCertificate(identity, &certificate); const void *certs[] = {certificate}; CFArrayRef cerArray = CFArrayCreate(kCFAllocatorMalloc, certs, 1, NULL); return [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray *)cerArray persistence:NSURLCredentialPersistencePermanent]; } + (BOOL)extractIdentity:(SecIdentityRef*)outIdentity fromPKCS12Data:(NSData *)inPKCS12Data { OSStatus securityError = errSecSuccess; //client certificate password NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"tc888888" 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; return YES; } else { NSLog(@"Failedwith error code %d",(int)securityError); return NO; } } @end