ios基础知识

SSL详解(个人理解)

2017-02-08  本文已影响843人  xiaoliang1
howsslwork_clip_image002.gif
一次完整的https请求大概是这样的。
1.客户端服务器发出https,请求。
2.服务器发送自己信息到客户端,包括服务器端公钥(.car证书文件) 等信息。
3.客户端根据服务器发回来的 公钥 去自己钥匙串里面匹配本地证书(公钥),来判断证书是否过期,可用等。如果本地证书没有的话。就会弹出警告改证书不被信任,询问用户是否要安装这个证书。一旦安装,下次访问的时候再去自己的钥匙串里面去找的时候就能找到了。
4.客户单服务器的证书加密随机值发给服务器(这个随机值在TCP协议里面会自动帮我加。OSX内核部分,我们都无法接触到。这个值我们改不了)。
5.服务器用自己的 私钥 解密发回来,客户端校验这个随机值是否正确。双方信任对方。

以上就是一次https握手过程。完成握手以后,就用公钥加密传输内容了。
举个例子:

1EA0B000-2937-4201-ABEE-D97CBE362D6A.jpg

查看百度的https证书。苹果一般会自动更新自己 钥匙串 里面的权威机构的证书。

QQ20170208-114019@2x.png

以上是苹果钥匙串里已经安装的证书。

QQ20170208-114134@2x.png QQ20170208-114222@2x.png

以上是百度的买的证书服务商。查了一下是比利时的。

这里说一下,我们开发中用到https。(ios9开始默认开始https)
一般只要你买的证书大部分都是直接过。客户端什么都不用做,只要把http改https就行了。你买的权威机构证书,手机端默认在钥匙串里面安装了默认信任机构的证书。(特别权威的https在访问的网址那里会有绿色符号)。所以网上说了一堆什么什么的,都不用管只要你用权威机构的,客户端什么都不用做。服务器端我就不知道了。阿里云貌似的就不用。客户端直接改https就行了。

另外,费事的就是自制的证书了。苹果钥匙串里没有你的证书,不信任你。这时候可能要做一些功夫了。

NSURLConnection的代理方法里面默认的https大概代码是这样的:

// Now start the connection
NSURL * httpsURL = [NSURL URLWithString:@"https://www.baidu.com"];
self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];
     
//回调
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //1)获取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
     
    //2)SecTrustEvaluate对trust进行验证
    OSStatus status = SecTrustEvaluate(trust, &result);
    if (status == errSecSuccess &&
        (result == kSecTrustResultProceed ||
        result == kSecTrustResultUnspecified)) {
         
        //3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
         
    } else {
     
        //5)验证失败,取消这次验证流程
        [challenge.sender cancelAuthenticationChallenge:challenge];
         
  }
}

在一般购买的证书。这个代理根本不用实现。啥都不用做。

自制的证书代码是这样的:

//先导入证书
NSString * cerPath = ...; //证书的路径
NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
self.trustedCertificates = @[CFBridgingRelease(certificate)];
//回调
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //1)获取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    //注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);
    //2)SecTrustEvaluate会查找前面SecTrustSetAnchorCertificates设置的证书或者系统默认提供的证书,对trust进行验证
    OSStatus status = SecTrustEvaluate(trust, &result);
    if (status == errSecSuccess &&
        (result == kSecTrustResultProceed ||
        result == kSecTrustResultUnspecified)) {
         
        //3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
         
    } else {
     
        //5)验证失败,取消这次验证流程
        [challenge.sender cancelAuthenticationChallenge:challenge];
         
  }
}

在老版本的ios系统里 可能 不实现这个方法没问题。在新版本的ios系统里,恐怕不行。

为了做一次实验。用百度写了一个小demo.

QQ20170208-114134@2x.png QQ20170208-122708@2x.png QQ20170208-122726@2x.png

找到刚刚百度用的证书,把他导出来然后添加项目工程了。用的时候,别用代理里面返回的证书信息。别别去钥匙串里面查找了,直接用我导的这个证书。

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"11111" ofType:@"cer"]; //证书的路径
    NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
    SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
    //    self.trustedCertificates = @[CFBridgingRelease(certificate)];
    
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    //注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)@[CFBridgingRelease(certificate)]);
    //2)SecTrustEvaluate会查找前面SecTrustSetAnchorCertificates设置的证书或者系统默认提供的证书,对trust进行验证
    NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
    [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
    
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"%@",response);
    
}

最后我们发现我们也能收到response.

QQ20170208-123327@2x.png

所以。本地证书和服务器证书是可以互相校验的。放哪无所谓。只不过默认去钥匙串里面找。

QQ20170208-123619@2x.png
Charles这个工具用过的都知道。这里直接叫青花瓷吧。
他是怎么工作的呢。
他就像客户端服务器之间的一个中间人。我们叫他代理服务器吧。
文章的开始就说了https请求的过程。
青花瓷在第二步开始的时候就把青花瓷自己的 公钥 返回给客户端了。
客户端请求信息作为自己的信息 请求去访问服务器
重新屡一下:

1.客户端发送请求到青花瓷
2青花瓷发送客户端请求到服务器
3.服务器返回 公钥信息到青花瓷
4.青花瓷把自己的 公钥 发送给客户端
5.客户端认证青花瓷的证书。
6.客户端青花瓷公钥 加密随机值发给青花瓷
7.青花瓷服务器公钥加密随机值发给 服务器
8.服务器用自己的私钥解密随机值,然后发给青花瓷
9.青花瓷用自己的私钥加密随机值,然后发送给客户端
10.客户端对比。完成握手。
11.青花瓷对比。完整握手。
以后。客户端就用青花瓷公钥加密数据发给青花瓷青花瓷用自己私钥解密,展现给我们看。然后用服务器公钥加密已经解密的数据发给服务器
就这样,帮我们拦截所有请求。

这也是为什么青花瓷拦截https时客户端要安装一份青花瓷的公钥证书,这样在第5步时候,客户端钥匙串找证书的时候正好能找到。

目前大部分app的https都能抓到数据。
但是某些apps 用自己的一套加密加密,然后用https加密传传输。所以有时候,我们在青花瓷看到也是加密数据。

有图有证据:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    //1)获取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    
    SecKeyRef  xxx =SecTrustCopyPublicKey(trust);
    
    SecCertificateRef  pppp = SecTrustGetCertificateAtIndex(trust,0);
    
    //2)SecTrustEvaluate对trust进行验证
}

未用青花瓷时:证书信息

QQ20170208-130351@2x.png

青花瓷时:证书信息

QQ20170208-130513@2x.png

两个证书明显不一样了。

木有木发现appstore有部分请求,用青花瓷拦截不到。
因为在第5步的时候,苹果没有根据服务器返回的证书来加密数据。而是直接用自己已知公钥证书加密返回,这样即使你钥匙串里面安装的别人的证书的时候也没有用了。

代码类似上面“用百度写了一个小demo”那里的代码:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"11111" ofType:@"cer"]; //证书的路径
    NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
    SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
    //    self.trustedCertificates = @[CFBridgingRelease(certificate)];
    
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    //注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)@[CFBridgingRelease(certificate)]);
    //2)SecTrustEvaluate会查找前面SecTrustSetAnchorCertificates设置的证书或者系统默认提供的证书,对trust进行验证
    NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
    [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
 
}

不去判断公钥证书是否安装、是否过期,也不去查找。直接用我知道的公钥证书去加密。这样就可以防止青花瓷做中间人拦截数据。(不查找,直接用已知的公钥证书和发过来的公钥证书对比信息,用服务器发过来的公钥信息查找证书直接判断过期、可用。还是有风险的)
这是青花瓷拦截不到了

QQ20170208-132115@2x.png

猜测苹果可能在这里做了判断。

保险的做法用了https之外,吧数据用自己的一套加密机制在加密一遍。

几个测试例子:
https://pan.baidu.com/s/1i5cB6Yh
用到某个测试就有个注释去掉就行了

参考文章:
http://www.cocoachina.com/ios/20150811/12947.html

上一篇下一篇

猜你喜欢

热点阅读