標籤:dom nsarray 項目組 bundle 地方 ast 安全 interval www
前幾日,項目組出於安全形度的考慮,要求項目中的請求使用https請求,因為是企業內部使用的app,因此使用了自簽名的認證,而自簽名的認證是不受信任的,所以我們就需要自己來做認證的驗證,包括伺服器驗證用戶端的認證和我們要信任伺服器的認證,SSL雙向認證的原理我這裡就不贅述了,這裡提供兩篇部落格
iOS安全系列之一:HTTPS: http://oncenote.com/2014/10/21/Security-1-HTTPS/
iOS安全系列之二:HTTPS進階: http://oncenote.com/2015/09/16/Security-2-HTTPS2/
從開始準備到調試成功一共用了兩天,查閱了很多網上提供的解決辦法,最後發現很多解決辦法都缺少了一個很重要的過程,就是將用戶端的認證發送到伺服器,讓伺服器驗證我們的身份.如果沒有這一步,那麼我們的請求就會直接被伺服器拒絕導致請求失敗.
話不多說,相信說多了也沒人看..[手動滑稽]直接上代碼,我這裡用了afn3.0
準備工作:
首先要匯入兩個認證, 是兩個 一個是用戶端使用的.p12的認證,另外一個是伺服器端的.cer的認證,將這兩個認證拖入工程中,注意要添加target
然後要配置ATS,ATS的配置我也提供一篇部落格
iOS 10 適配 ATS: http://www.jianshu.com/p/36ddc5b009a7
建立一個類,繼承AFHTTPSessionManager, 我這裡重寫了initWithURL
1 - (instancetype)initWithBaseURL:(NSURL *)url { 2 self = [super initWithBaseURL:url]; 3 if (self) { 4 5 self.requestSerializer.timeoutInterval = 25; 6 [self.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"]; 7 [self.requestSerializer setValue:url.absoluteString forHTTPHeaderField:@"Referer"]; 8 9 self.securityPolicy = [self getFreeCustomSecurityPolicy];10 11 self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"multipart/form-data", @"application/json", @"text/html", @"image/jpeg", @"image/png", @"application/octet-stream", @"text/json", nil];12 self.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;13 14 [self setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {15 NSLog(@"setSessionDidBecomeInvalidBlock error : %@",error);16 }];17 if ([url.absoluteString hasPrefix:@"https"]) {//當請求是https時,我們就需要做18 __weak typeof(self) weakSelf = self;19 [self setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing _Nullable * _Nullable credential) {20 NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;21 __autoreleasing NSURLCredential * credentialSelf =nil;22 if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {//如果有受信任的認證,如果是自簽名的認證,這裡會是NSURLAuthenticationMethodClientCertificate
23 if([weakSelf.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
24 credentialSelf = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];25 if(credentialSelf) {26 disposition = NSURLSessionAuthChallengeUseCredential;27 } else {28 disposition = NSURLSessionAuthChallengePerformDefaultHandling;29 }30 } else {31 disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;32 }33 } else {//當接收到的是NSURLAuthenticationMethodClientCertificate,我們就將我們的認證發送給伺服器讓其驗證我們的身份34 // client authentication35 SecIdentityRef identity = NULL;36 SecTrustRef trust = NULL;37 NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];38 NSFileManager *fileManager =[NSFileManager defaultManager];39 if(![fileManager fileExistsAtPath:p12])40 {41 NSLog(@"client.p12:not exist");42 } else {43 NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];44 45 if ([weakSelf extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])46 {47 SecCertificateRef certificate = NULL;48 SecIdentityCopyCertificate(identity, &certificate);49 const void*certs[] = {certificate};50 CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);51 credentialSelf =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];52 disposition = NSURLSessionAuthChallengeUseCredential;53 }54 }55 }56 *credential = credentialSelf;57 return disposition;58 }];59 }60 }61 return self;62 }63 - (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {64 OSStatus securityError = errSecSuccess;65 //client certificate password66 NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"password" forKey:(__bridge id)kSecImportExportPassphrase];67 CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);68 securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);69 70 if(securityError == 0) {71 CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);72 const void*tempIdentity =NULL;73 tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);74 *outIdentity = (SecIdentityRef)tempIdentity;75 const void*tempTrust =NULL;76 tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);77 *outTrust = (SecTrustRef)tempTrust;78 } else {79 NSLog(@"Failedwith error code %d",(int)securityError);80 return NO;81 }82 return YES;83 }84 - (AFSecurityPolicy *)getFreeCustomSecurityPolicy {85 AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];86 NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];87 NSData *certData = [NSData dataWithContentsOfFile:cerPath];88 NSSet *certificateSet = [[NSSet alloc] initWithObjects:certData, nil];89 [securityPolicy setAllowInvalidCertificates:YES];90 [securityPolicy setPinnedCertificates:certificateSet];91 [securityPolicy setValidatesDomainName:NO];92 93 return securityPolicy;94 }
這樣設定好了之後就可以發送使用自我簽署憑證的https請求了
希望可以協助到遇到自我簽署憑證無法發送請求的同學
代碼不太優雅,輕噴,如有錯誤的地方歡迎指正
郵箱:[email protected]
AFNetWorking3.0使用 自我簽署憑證的https請求