一、背景:
苹果从IOS9.0以后就要求使用https,今年发布说的是在2017年1月1日后,所有上架的APP必须使用HTTPS(貌似目前推迟了)。不论怎么说,使用https时迟早的事情,之前通过在info.plist文件中设置 NSAppTransportSecurity为NSAllowsArbitraryLoads的方式不起作用了。这篇文章主要就是介绍在IOS中如何将http改造成https。
二、关于https:
可以简单的理解https就是http的安全版本,https实质是http+SSL;就在http请求的基础上加上一层安全措施,这样抓包工具抓到的数据显示的就是乱码,而不是明文方式了。
三、IOS中的AFNetworking使用https:
1.需要服务端提供认证证书.crt文件,然后自己导出成.cer文件
2.将导出的cer证书加入到项目中,注意勾选相应的target不然可能获取证书路径为nil
3.通过cer证书生成证书校验的安全策略
4.在AFNetworking的网络请求中设置安全策略:[_operationManager setSecurityPolicy:[CertificatehttpsTools customSecurityPolicy]];
5.通过抓包工具Charles检验请求和返回的内容是否加密
证书校验安全策略的工具类:
#import <Foundation/Foundation.h>
#import "AFNetworking.h"
@interface HttpsCertificateTools : NSObject
/**
自定义证书安全策略
@return 返回自定义的证书安全策略
*/
+ (AFSecurityPolicy*)customSecurityPolicy;
@end
#import "HttpsCertificateTools.h"
@implementation HttpsCertificateTools
+ (AFSecurityPolicy*)customSecurityPolicy{
// /先导入证书
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"服务端证书名称" ofType:@"cer"];//证书的路径
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
// NSString *aString = [[NSString alloc] initWithData:certData encoding:NSUTF8StringEncoding];
// AFSSLPinningModeCertificate 使用证书验证模式
// AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
// 如果是需要验证自建证书,需要设置为YES
securityPolicy.allowInvalidCertificates = YES;
更多免费的Python学习资料
进QQ群 688244617
群里还有小伙伴跟你一起交流学习
//validatesDomainName 是否需要验证域名,默认为YES;
//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
//置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
//如置为NO,建议自己添加对应域名的校验逻辑。
securityPolicy.validatesDomainName = NO;
// securityPolicy.validatesCertificateChain = NO;
// requestOperationManager.securityPolicy = securityPolicy;
securityPolicy.pinnedCertificates = @[certData];
return securityPolicy;
}
@end
AFNetworking在网络请求的时候添加安全策略:
_operationManager = [AFHTTPRequestOperationManager manager];
//https协议
[_operationManager setSecurityPolicy:[HttpsCertificateTools customSecurityPolicy]];
下面方法是双向认证需要实现的,如果你只做单向认证的话不用管:
在AFURLConnectionOperation.m中修改willSendRequestForAuthenticationChallenge方法:
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// if (self.authenticationChallenge) {
// self.authenticationChallenge(connection, challenge);
// return;
// }
//
// if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
// NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
// } else {
// [[challenge sender] cancelAuthenticationChallenge:challenge];
// }
// } else {
// if ([challenge previousFailureCount] == 0) {
// if (self.credential) {
// [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
// } else {
// [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
// }
// } else {
// [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
// }
// }
NSString *thePath = [[NSBundle mainBundle] pathForResource:@"服务返回的客户端证书" ofType:@"p12"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
SecIdentityRef identity = NULL;
更多免费的Python学习资料
进QQ群 688244617
群里还有小伙伴跟你一起交流学习
// extract the ideneity from the certificate
[self extractIdentity :inPKCS12Data :&identity];
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate (identity, &certificate);
const void *certs[] = {certificate};
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
// create a credential from the certificate and ideneity, then reply to the challenge with the credential
//NSLog(@"identity=========%@",identity);
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:nil persistence:NSURLCredentialPersistencePermanent];
credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
将方法:
(OSStatus)extractIdentity:(CFDataRef)inP12Data :(SecIdentityRef*)identity {
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("showbaby123456");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inP12Data, options, &items);
if (securityError == 0){
CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
}
if (options) {
CFRelease(options);
}
return securityError;
}
添加到类中。
下载抓包工具Charles,设置手机的代理和请求地址,请求数据然后,查看请求和返回的数据是否加密。
原文:https://blog.csdn.net/guobing19871024/article/details/53841373