CocoaHttpServer设置https


热烈欢迎,请直接点击!!!

进入博主App Store主页,下载使用各个作品!!!

注:博主将坚持每月上线一个新app!!!

客户端向服务器发起需求 ,服务器把证书发给客户端,客户端验证下证书是否合法,然后用证书的数据加密传输数据给服务器,服务器解密

生成证书步骤

// 第一步,为服务器端和客户端准备公钥、私钥
# 生成服务器端私钥
openssl genrsa -out server.key 1024
# 生成服务器端公钥
openssl rsa -in server.key -pubout -out server.pem

// 第二步,生成 CA 证书
# 生成 CA 私钥
openssl genrsa -out ca.key 1024
openssl rsa -in ca.key -pubout -out ca.pem
# X.509 Certificate Signing Request (CSR) Management.
openssl req -new -key ca.key -out ca.csr
# X.509 Certificate Data Management.
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt

// 第三步,生成服务器端证书
# 服务器端需要向 CA 机构申请签名证书,在申请签名证书之前依然是创建自己的 CSR 文件
openssl req -new -key server.key -out server.csr
# 向自己的 CA 机构申请证书,签名过程需要 CA 的证书和私钥参与,最终颁发一个带有 CA 签名的证书
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt

// 第四步,生成cer文件
# 使用openssl 进行转换
openssl x509 -in server.crt -out server.cer -outform der
openssl x509 -in ca.crt -out ca.cer -outform der

// 将证书导出成浏览器支持的.p12格式,记得导出密码
openssl pkcs12 -export -clcerts -in server.pem -inkey server.key -out server.p12

在第二步时会出来一个填写资料的界面:

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Guangdong
Locality Name (eg, city) []:Shenzhen
Organization Name (eg, company) [Internet Widgits Pty Ltd]:cnblogs
Organizational Unit Name (eg, section) []:strengthen
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:newsagency@163.com

配置服务器

为了方便,我是以mac本地电脑做服务器,使用的是XAMPP搭建的服务器使用的是apache。在其他服务器上应该就是文件路径位置不一样,其他应该是一样的。如果有些服务器没开启ssl,可以网上搜索怎么开启。

修改httpd-ssl.conf文件 把server.crt和server.key的路径修改对就好了
SSLCertificateFile /apache/conf/server.crt  
SSLCertificateKeyFile /apache/conf/server.key 

文件里设置一下开启 https 功能:

 
- (BOOL)isSecureServer
{
 
    // Create an HTTPS server (all connections will be secured via SSL/TLS)
    return YES;
}
 
/**
 * This method is expected to returns an array appropriate for use in kCFStreamSSLCertificates SSL Settings.
 * It should be an array of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef.
 **/
- (NSArray *)sslIdentityAndCertificates
{
    SecIdentityRef identityRef = NULL;
    SecCertificateRef certificateRef = NULL;
    SecTrustRef trustRef = NULL;
 
    NSString *thePath = [[NSBundle mainBundle] pathForResource:@"my" ofType:@"p12"];
    NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
    CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data);
    CFStringRef password = CFSTR("123");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
 
    OSStatus securityError = errSecSuccess;
    securityError =  SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
    if (securityError == 0) {
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
        const void *tempIdentity = NULL;
        tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
        identityRef = (SecIdentityRef)tempIdentity;
        const void *tempTrust = NULL;
        tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
        trustRef = (SecTrustRef)tempTrust;
    } else {
        NSLog(@"Failed with error code %d",(int)securityError);
        return nil;
    }
 
    SecIdentityCopyCertificate(identityRef, &certificateRef);
    NSArray *result = [[NSArray alloc] initWithObjects:(id)CFBridgingRelease(identityRef),   (id)CFBridgingRelease(certificateRef), nil];
 
    return result;
}

总结一下配置过程中可能碰到配置完成后还是无法访问https的问题:

1、可以判断p12文件是否导入项目,以及路径是否寻找正确

2、确保iOS项目中代码所有的请求都为https请求,因为ssl配置服务器的时候,如果服务器中含有http请求,同样会出现不安全的ssl情况

3、在GCDAsyncSocket.m文件中注释该段代码

//    if (value)
//    {
//        NSAssert(NO, @"Security option unavailable - kCFStreamSSLLevel"
//                     @" - You must use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMax");
//
//        [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLLevel"]];
//        return;
//    }

配置AFNetworking

+ (AFSecurityPolicy*)customSecurityPolicy
{
    // /先导入证书
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];//证书的路径
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

    // AFSSLPinningModeCertificate 使用证书验证模式
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

    // allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
    // 如果是需要验证自建证书,需要设置为YES
    securityPolicy.allowInvalidCertificates = YES;

    //validatesDomainName 是否需要验证域名,默认为YES;
    //假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
    //置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
   //如置为NO,建议自己添加对应域名的校验逻辑。
    securityPolicy.validatesDomainName = NO;

    securityPolicy.pinnedCertificates = @[certData];

    return securityPolicy;
}

+ (void)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id))success failure:(void (^)(NSError *))failure
{
    // 1.获得请求管理者
    AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
    // 2.申明返回的结果是text/html类型
    mgr.responseSerializer = [AFHTTPResponseSerializer serializer];

    // 加上这行代码,https ssl 验证。
    [mgr setSecurityPolicy:[NetworkHelpManager customSecurityPolicy]];

    // 3.发送POST请求
    [mgr POST:url parameters:params
  success:^(NSURLSessionDataTask *operation, id responseObj) {
          if (success) {
            success(responseObj);
         }
      } failure:^(NSURLSessionDataTask *operation, NSError *error) {
          if (failure) {
              failure(error);
          }
      }];
}

info.plist,配置白名单


    NSAllowsArbitraryLoads
    <false/>
    NSExceptionDomains
    
        cer文件中查看
        
            NSExceptionAllowsInsecureHTTPLoads
            <true/>
            NSExceptionMinimumTLSVersion
            <string>TLSv1.0string>
            NSExceptionRequiresForwardSecrecy
            <false/>
            NSIncludesSubdomains
            <true/>
        
    
iOS