由于ios9之后强制的https请求,原来的http请求所占的市场比率也越来越小。在网上查了几十篇类似的https请求的文章,诸如此类的问题很多,但是回答者含糊其辞, 都不尽如人意,经过几天的探索,写下这篇文章,希望可以帮到有这方便需求的开发者。
首先,需要知道什么是https,以此共勉。
1.HTTPS和HTTP的区别
https协议需要到CA申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https则是具有安全性的SSL加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
2.非对称加密算法
这种加密或许理解起来比较困难,这种加密指的是可以生成公钥和私钥。凡是公钥加密的数据,公钥自身不能解密,而需要私钥才能解密;凡是私钥加密的数据,私钥不能解密,需要公钥才能解密。这种算法事实上有很多,常用的是RSA,其基于的数学原理是两个大素数的乘积很容易算,而拿到这个乘积去算出是哪两个素数相乘就很复杂了,具体原理有兴趣可以自行研究。
非对称加密相比对称加密更加安全,但也存在两个明显缺点:
CPU计算资源消耗非常大。一次完全TLS握手,密钥交换时的非对称解密计算量占整个握手过程的90%以上。而对称加密的计算量只相当于非对称加密的0.1%,如果应用层数据也使用非对称加解密,性能开销太大,无法承受。
非对称加密算法对加密内容的长度有限制,不能超过公钥长度。比如现在常用的公钥长度是2048位,意味着待加密内容不能超过256个字节。
所以公钥加密目前只能用来作密钥交换或者内容签名,不适合用来做应用层传输内容的加解密。
3 身份认证(CA数字证书)
https协议中身份认证的部分是由数字证书来完成的,证书由公钥、证书主体、数字签名等内容组成,在客户端发起SSL请求后,服务端会将数字证书发给客户端,客户端会对证书进行验证,并获取用于秘钥交换的非对称密钥。
数字证书有两个作用:
身份授权。确保浏览器访问的网站是经过CA验证的可信任的网站。
分发公钥。每个数字证书都包含了注册者生成的公钥。在SSL握手时会通过certificate消息传输给客户端。
申请一个受信任的数字证书通常有如下流程:
终端实体(可以是一个终端硬件或者网站)生成公私钥和证书请求。
RA(证书注册及审核机构)检查实体的合法性。如果个人或者小网站,这一步不是必须的。
CA(证书签发机构)签发证书,发送给申请者。
证书更新到repository(负责数字证书及CRL内容存储和分发),终端后续从repository更新证书,查询证书状态等。
好了,废话不多说,上代码。
首先:如果是双向认证,需要先导入服务器的证书,ASI我不知道怎么做,但是AFN需要新建一个类
- (AFSecurityPolicy*)customSecurityPolicy {
NSString *cerPath = [[NSBundle mainBundle] pathForResource:fuwuqi ofType:];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
[securityPolicy setAllowInvalidCertificates:YES];
[securityPolicy setPinnedCertificates:@[certData]];
/**** SSL Pinning ****/
return securityPolicy;
} ps:这个类其实建不建都无所谓,但是一定是在请求之前先将这个securityPolicy创建出来
所有的服务器返回的请求都是在- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
这个方法中进行验证的,所以需要 重写这个方法
NSString *thePath = [[NSBundle mainBundle] pathForResource:kehuduan ofType:];
NSLog(@"thePath===========%@",thePath);
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
SecIdentityRef identity = NULL;
// 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 securityError = errSecSuccess;
CFStringRef password = CFSTR("123456");
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;
这样将服务器返回的证书及进行通过公钥解密,与本地的证书进行对比,如果通过则继续下一步,生成一个验证签,携带本地客户端证书发给服务器,服务器进行验证,这就是的双向验证。当然,这里用到的都是自签证书,如果是购买的CA证书,则是不需要验证
securityPolicy那一步的,
欢迎大家点评。