• https 安全验证问题


    最近为了满足苹果的 https 要求, 经过努力终于写出了方法

    验证 SSL 证书是否满足 ATS 要求

    nscurl --ats-diagnostics --verbose https://你的域名

    PASS 符合要求

    输出满足 ATS 的证书

    openssl s_client -connect 你的域名:443 </dev/null 2>/dev/null | openssl x509 -outform DER > https.cer

    1. 针对 AFNetWorking (2.6.0之前的版本)

    AFSecurityPolicy分三种验证模式:

    AFSSLPinningModeNone

    这个模式表示不做SSL pinning,
    只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书就不会通过。

    AFSSLPinningModeCertificate

    这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。 

    AFSSLPinningModePublicKey

    这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,
    只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。

     1 // 正对是 app 新人的机构发布的 SSL 证书
     2 
     3 AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
     4 
     5 //allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO //如果是需要验证自建证书,需要设置为YES
     6 
     7 securityPolicy.allowInvalidCertificates = YES;
     8 
     9 //validatesDomainName 是否需要验证域名,默认为YES;
    10 
    11 //假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
    12 
    13 //置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
    14 
    15 //如置为NO,建议自己添加对应域名的校验逻辑。
    16 
    17 securityPolicy.validatesDomainName = YES;
    18 
    19 //validatesCertificateChain 是否验证整个证书链,默认为YES
    20 
    21 //设置为YES,会将服务器返回的Trust Object上的证书链与本地导入的证书进行对比,这就意味着,假如你的证书链是这样的:
    22 
    23 //GeoTrust Global CA // Google Internet Authority G2
    24 
    25 // *.google.com //那么,除了导入*.google.com之外,还需要导入证书链上所有的CA证书(GeoTrust Global CA, Google Internet Authority G2);
    26 
    27 //如是自建证书的时候,可以设置为YES,增强安全性;假如是信任的CA所签发的证书,则建议关闭该验证,因为整个证书链一一比对是完全没有必要(请查看源代码);
    28 
    29 securityPolicy.validatesCertificateChain = NO;  // 2.6.0之前不需要, 之后需要
    30 
    31 requestOperationManager.securityPolicy = securityPolicy;
    32 
    33 // 如实自建的证书
    34 
    35 还需要把证书导入本地工程中, 并天添加以下代码
    36 
    37 NSData *cerData = [self getSSLCerByCerName:@"本地SSL证书的名字 "];
    38 [securityPolicy setPinnedCertificates:@[cerData]];
    39 
    40 // 获取 SSL 证书
    41 
    42 + (NSData *)getSSLCerByCerName:(NSString *)cerName {
    43 NSString *cerPath = [[NSBundle mainBundle] pathForResource:cerName
    44 ofType:@"cer"];
    45 NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    46 return certData;
    47 }
    48 
    49  

    2. 针对 NSURLConnection

    验证证书的API

    相关的Api在Security Framework中,验证流程如下:

    1). 第一步,先获取需要验证的信任对象(Trust Object)。这个Trust Object在不同的应用场景下获取的方式都不一样,对于NSURLConnection来说,是从delegate方法-connection:willSendRequestForAuthenticationChallenge:回调回来的参数challenge中获取([challenge.protectionSpace serverTrust])。

    2). 使用系统默认验证方式验证Trust Object。SecTrustEvaluate会根据Trust Object的验证策略,一级一级往上,验证证书链上每一级数字签名的有效性(上一部分有讲解),从而评估证书的有效性。

    3). 如第二步验证通过了,一般的安全要求下,就可以直接验证通过,进入到下一步:使用Trust Object生成一份凭证([NSURLCredential credentialForTrust:serverTrust]),传入challenge的sender中([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])处理,建立连接。

    4). 假如有更强的安全要求,可以继续对Trust Object进行更严格的验证。常用的方式是在本地导入证书,验证Trust Object与导入的证书是否匹配。更多的方法可以查看Enforcing Stricter Server Trust Evaluation,这一部分在讲解AFNetworking源码中会讲解到。

    5). 假如验证失败,取消此次Challenge-Response Authentication验证流程,拒绝连接请求。

    ps: 假如是自建证书的,则不使用第二步系统默认的验证方式,因为自建证书的根CA的数字签名未在操作系统的信任列表中。

    iOS授权验证的API和流程大概了解了,下面,我们看看在NSURLConnection中的代码实现:

     1 // NSURLConnection Https 安全验证问题
     2 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace{
     3 return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
     4 }
     5 // 针对的是自建证书, 未受安全机构信任
     6 - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
     7 static CFArrayRef certs;
     8 if (!certs) {
     9 NSData*certData =[NSData dataWithContentsOfFile:[[NSBundle mainBundle]
    10 pathForResource:@"本地工程中的SSL证书的名字" ofType:@"cer"]];
    11 SecCertificateRef rootcert
    12 =SecCertificateCreateWithData(kCFAllocatorDefault,CFBridgingRetain(certData));
    13 const void *array[1] = { rootcert };
    14 certs = CFArrayCreate(NULL, array, 1, &kCFTypeArrayCallBacks);
    15 CFRelease(rootcert); 
    16 }
    17 
    18 SecTrustRef trust = [[challenge protectionSpace] serverTrust];
    19 // 针对一个证书对应多个域名, 无需验证域名
    20 NSMutableArray *policies = [NSMutableArray array];
    21 // BasicX509 不验证域名是否相同
    22 SecPolicyRef policy = SecPolicyCreateBasicX509();
    23 [policies addObject:(__bridge_transfer id)policy];
    24 SecTrustSetPolicies(trust, (__bridge CFArrayRef)policies);
    25 int err;
    26 SecTrustResultType trustResult = 0;
    27 err = SecTrustSetAnchorCertificates(trust, certs);
    28 if (err == noErr) {
    29 err = SecTrustEvaluate(trust,&trustResult);
    30 }
    31 CFRelease(trust);
    32 // kSecTrustResultUnspecified: 系统隐式地信任这个证书
    33 // kSecTrustResultProceed: 用户加入自己的信任锚点,显式地告诉系统这个证书是值得信任的
    34 BOOL trusted = (err == noErr)
    35 && ((trustResult == kSecTrustResultProceed)
    36 || (trustResult == kSecTrustResultUnspecified));
    37 if (trusted) {
    38 [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
    39 forAuthenticationChallenge:challenge];
    40 }else{
    41 [challenge.sender cancelAuthenticationChallenge:challenge];
    42 }
    43 }
    44 
    45 
    46 
    47 // SSL 证书是经过信任的机构授权的, 不用再把证书存放在本地
    48 - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    49 //1)获取trust object
    50 SecTrustRef trust = challenge.protectionSpace.serverTrust;
    51 SecTrustResultType result;
    52 
    53 // 针对一个证书对应多个域名, 无需验证域名
    54 NSMutableArray *policies = [NSMutableArray array];
    55 // BasicX509 不验证域名是否相同
    56 SecPolicyRef policy = SecPolicyCreateBasicX509();
    57 [policies addObject:(__bridge_transfer id)policy];
    58 SecTrustSetPolicies(trust, (__bridge CFArrayRef)policies);
    59 
    60 //2)SecTrustEvaluate对trust进行验证
    61 OSStatus status = SecTrustEvaluate(trust, &result);
    62 if (status == errSecSuccess &&
    63 (result == kSecTrustResultProceed ||
    64 result == kSecTrustResultUnspecified)) {
    65 //3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
    66 NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
    67 [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
    68 
    69 } else {
    70 //5)验证失败,取消这次验证流程
    71 [challenge.sender cancelAuthenticationChallenge:challenge];
    72 }
    73 }

    ######

    对于满足https 需求的 SSL 则无须做任何多余的操作, 正常发送请求就可以了.

  • 相关阅读:
    《SQL Server 2008从入门到精通》--20180628
    2019国赛
    [蓝桥杯2020] 湖北省省赛
    2019第十届蓝桥杯B组决赛题解第五题
    试题 算法提高 分解质因数
    大数相加减
    试题 算法提高 汉诺塔
    试题 算法提高 欧拉函数
    试题 算法提高 天天向上-dp
    算法训练 猴子吃包子-构造
  • 原文地址:https://www.cnblogs.com/lurenq/p/6202182.html
Copyright © 2020-2023  润新知