具有真实证书的Ocj-C安全套接字服务器 - SSL协议错误(基于GCDAsyncSockets)

时间:2015-09-01 14:36:17

标签: macos sockets ssl gcdasyncsocket

我一直在讨论一个奇怪的问题 - 这似乎是未说出口的(或者说是完全不同的)。 令人惊讶的是,我找不到一个HTTPS示例 - 我找到的所有内容都有'todo'!

简而言之:

我正在尝试使用有效的证书创建一个使用TLS的“服务器”。

问题:

成功导入证书后,设置GCDAsyncSockets的设置,然后调用startTLS,连接将立即删除 'SecureTransport error -9800' - Chrome称之为'ERR_SSL_PROTOCOL_ERROR'

测试:

  1. 运行服务器。
  2. 通过Chrome或Safari(https://localhosthttps://localhost:somefreeport)进行连接。
  3. 尝试观察收到数据的页面请求。
  4. 在握手阶段观察失败。
  5. Chrome是我选择的测试平台,因为此练习稍后将处理WebSocket流量 - 因此我使用的是有效的证书,而非自签名。

    Long desc:

    使用GCDAsyncSocket监听端口,然后一旦我收到连接,我就接受那个套接字和'startTLS'来处理它。 (我不是专门使用GCDAsyncSocket,它只是我熟悉它在客户端套接字使用明文和TLS,以及服务器明文 - 它只是服务器TLS让我难倒)。

    证书是已知公共CA(Comodo或GoDaddy)的签名证书 - 我已成功使用它.Net 下面的源代码将成功打开证书并报告对等名称。 然而,.Net简化了很多这方面的工作,所以比较起来并不容易。 (加载cert并传入x509对象,然后应用于新套接字。在这里,我还必须定义密码和SSL版本。)

    简化代码示例:

    - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
    {   
        //Get certificate
        NSString* p12Path = [[NSBundle bundleForClass:[self class]] pathForResource:@"mySignedCert"
                                                                             ofType:@"pfx"];
        CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain([[NSData alloc] initWithContentsOfFile:p12Path]);
    
        CFStringRef password = CFSTR("myCertPassword"); //Cert Password
        const void *keys[] = { kSecImportExportPassphrase };
        const void *values[] = { password };
        CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    
        OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
        CFRelease(options);
        CFRelease(password);
    
        if(securityError == errSecSuccess)
            NSLog(@"Success opening p12 certificate."); //All good!
        else
            return nil;
    
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                                                      kSecImportItemIdentity);
    
        SecIdentityRef  certArray[1] = { myIdent };
        CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
    
        //Build TLS settings dictionary
        [self willSecureWithSettings:settings]; //This just uses SSLGetNumberSupportedCiphers & SSLGetSupportedCiphers to create a cipher array and appends to settings
        settings[(NSString *)kCFStreamSSLCertificates] = (__bridge id)(myCerts);
        settings[(NSString *)kCFStreamSSLPeerName] = @"my.peer.address";
    
        /* Valid values for protocol types
         kSSLProtocolUnknown = 0,               no protocol negotiated/specified; use default
        kSSLProtocol3       = 2,                SSL 3.0
        kTLSProtocol1       = 4,                TLS 1.0
        kTLSProtocol11      = 7,                TLS 1.1
        kTLSProtocol12      = 8,                TLS 1.2
        kDTLSProtocol1      = 9,                DTLS 1.0
         */
    
        //Tried 0,2,4,7,8 - 9 throws another error, possibly unsupported.
        settings[GCDAsyncSocketSSLProtocolVersionMin] = [NSNumber numberWithInteger:8];
        settings[GCDAsyncSocketSSLProtocolVersionMax] = [NSNumber numberWithInteger:8];
    
        [newSocket startTLS:settings];
        [newSocket readDataWithTimeout:-1 tag:0];
    }
    

    我发现了很多旧的例子,没有一个适用(不使用较旧的GCDAsyncSocket或较旧的Xcode)。 我发现最接近后来的'GCDAsyncSocketSSLProtocolVersionMin / Max'设置为2,例如在Chrome中不支持。

    有没有人成功并愿意分享一些指示?

    亲切的问候,KF。

1 个答案:

答案 0 :(得分:1)

哇 - 我花了很多时间解决这个问题,然后到这里寻求提示。

在10分钟后计算出来!

我的更改:

//THIS! Tried prior, but GCDAsyncSocket docs stated not-needed, plus was getting different handshake errors.
settings[(NSString *)kCFStreamSSLIsServer] = [NSNumber numberWithBool:YES];

其他SO用户可能会觉得有用的其他更改:

//Apparently prefereable
settings[(NSString*)kCFStreamPropertyShouldCloseNativeSocket] = [NSNumber numberWithBool:YES];
//Above example was both matching for test. 4-8 was always preferable.
settings[GCDAsyncSocketSSLProtocolVersionMin] = [NSNumber numberWithInteger:4];
settings[GCDAsyncSocketSSLProtocolVersionMax] = [NSNumber numberWithInteger:8];

非常感谢 - 即使你是在寻找Rubber Ducking!