国际标准化组织(ISO)在1978年将计算机网络体系结构的通信协议划分为七层,自下而上依次为:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。其中每一层都规定有明确的服务及接口标准;把用户的应用程序作为最高层;除了最高层外,中间的每一层都向上一层提供服务,同时又是下一层的用户。
http(超文本传输协议)
HTTP是客户端浏览器或其他程序与Web服务器之间的应用层通信协议,HTTP通信的基本单位是报文。在Http工作之前,客户端通过网络和服务器建立链连接,该连接是通过TCP来完成的,一般TCP接口的端口号是80(传输层主要有两个协议,分别是 TCP 和 UDP)。
常见状态码
HTTP请求方法包括GET、POST、PUT、DELETE、PATCH、HEAD、
OPTIONS、TRACE。其中GET和POST最为常用。
GET和POST的区别
从语义角度来说,GET是从指定的资源请求数据,POST是向指定的资源提交要被处理的数据。通俗点将,GET是获取数据,POST是更新数据。
具体区别可以大致总结为一下几点:
(1) GET的URL可见,POST的URL不可见;
(2) GET请求可以被缓存,POST不会被缓存;
(3) GET请求可以保留在浏览器历史记录中,POST不会;
(4) GET请求有长度限制,2048个字节,POST长度没有限制;
(5) GET请求是幂等性的,POST是非幂等性的;
(6) GET请求的安全性没有POST高。
连接的建立需要通过三次握手
首先,介绍一下几个概念:
ACK:响应标识,1表示响应,连接建立成功之后,所有报文段ACK的值都为1
SYN:连接标识,1表示建立连接,连接请求和连接接受报文段SYN=1,其他情况都是0
FIN:关闭连接标识,1标识关闭连接,关闭请求和关闭接受报文段FIN=1,其他情况都是0,跟SYN类似
seq number:序号,一个随机数X,请求报文段中会有该字段,响应报文段没有
ack number:应答号,值为请求seq+1,即X+1,除了连接请求和连接接受响应报文段没有该字段,其他的报文段都有该字段
知道了上面几个概念后,看一下三次握手的具体流程:
第一次握手:建立连接请求。客户端发送连接请求报文段,将SYN置为1,seq为随机数x。然后,客户端进入SYN_SEND状态,等待服务器确认。
第二次握手:确认连接请求。服务器收到客户端的SYN报文段,需要对该请求进行确认,设置应答号ack=x+1(即客户端seq+1),同时设置连接标识SYN=1,响应标识ACK=1,序号seq=y。服务器一并发送给客户端,服务器进入SYN_RECV状态。
第三次握手:客户端收到SYN+ACK报文段,将ack设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕,客户端和服务券进入ESTABLISHED状态,完成Tcp三次握手。
断开连接需要四次挥手:
第一次挥手:客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。
第二次挥手:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
HTTPS(:Hyper Text Transfer Protocol over Secure Socket Layer)
HTTPS的建立流程
Https在建立连接之前,需要进行握手
1.客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
2.服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
3.客户端使用服务端返回的信息验证服务器的合法性,包括:
证书是否过期
发型服务器证书的CA是否可靠
返回的公钥是否能正确解开返回证书中的数字签名
服务器证书上的域名是否和服务器的实际域名相匹配
验证通过后,将继续进行通信,否则,终止通信
4.客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
5.服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。
6.服务器将选择好的加密方案通过明文方式返回给客户端
7.客户端接收到服务端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的密钥,使用服务端返回的公钥进行加密,将加密后的随机码发送至服务器
8.服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密密钥。
9.在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。
单向:客户端验证服务端的CA证书
双向:客户端验证服务器的CA证书,服务器验证客户端的p12证书
下面是AFNetworking的实现代码
manager.securityPolicy= [self customSecurityPolicy];
[self checkCredential:manager];
//启用服务器证书验证 (单向认证只需要实现此方法)
- (AFSecurityPolicy*)customSecurityPolicy
{
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"service" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName=NO;
return securityPolicy;
}
客户端请求验证 重写 setSessionDidReceiveAuthenticationChallengeBlock 方法
- (void)checkCredential:(AFURLSessionManager *)manager
{
[manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
}];
__weak typeof(manager)weakManager = manager;
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *credential =nil;
NSLog(@"authenticationMethod=%@",challenge.protectionSpace.authenticationMethod);
//判断是核验客户端证书还是服务器证书
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 基于客户端的安全策略来决定是否信任该服务器,不信任的话,也就没必要响应挑战
if([weakManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
// 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
NSLog(@"serverTrust=%@",challenge.protectionSpace.serverTrust);
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 确定挑战的方式
if (credential) {
//证书挑战 设计policy,none,则跑到这里
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
// client authentication
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"app"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
if ([self 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;
}];
}
验证客户端证书
+(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
OSStatus securityError = errSecSuccess;
//客户端证书密码
NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"p12证书密码"
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;
}
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。