iosobjective-ciphonegcdasyncsocketstarttls

GCDAsyncSocket startTLS does not work


I am able to connect to the desired socket with IP and port.

- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port {

    [sender startTLS:nil];

    if(self.createTCPSocketHelperDelegate && @selector(returnConnectedTCPSocket:forNASWithMacAddress:))
    {
        [self.createTCPSocketHelperDelegate returnConnectedTCPSocket:sender forNASWithMacAddress:_macAddress];
    }
}

I am sending TLS settings dictionary nil for as it uses default settings. It gives me the error as follows

Error Domain=kCFStreamErrorDomainSSL Code=-9806 "The operation couldn’t be completed. (kCFStreamErrorDomainSSL error -9806.)" UserInfo=0x17d92420 {NSLocalizedRecoverySuggestion=Error code definition can be found in Apple's SecureTransport.h}

I couldn't get what's wrong going on there. Please help me to provide some sample code which connect to SSL server and use TCP/TLS protocol.

Help would be appreciated. Thank you in advance. :)


Solution

  • I got the issue. Basically I couldn't complete SSL handshake, I found the way as follows:

    Initialize GCDAsyncSocket socket and connect to IP and Port as follows,

    NSError *error;
    
    _gcdAsyncTCPSocket  = [[GCDAsyncSocket alloc] initWithDelegate:self
                                                     delegateQueue:dispatch_get_main_queue()];
    
    _gcdAsyncTCPSocket.delegate = self;
    
    _ipAddress = ipAddress;
    
    _tcpPortNumber = portNumber;
    
    if(![_gcdAsyncTCPSocket connectToHost:ipAddress onPort:portNumber withTimeout:30 error:&error])
    {
        if(self.createTCPSocketHelperDelegate && @selector(failedToCreateTCPSocket))
        {
            [self.createTCPSocketHelperDelegate failedToCreateTCPSocket];
        }
    }else {
    
        NSMutableDictionary *settings = [[NSMutableDictionary alloc] init];
    
        [settings setObject:[NSNumber numberWithBool:YES]
                     forKey:GCDAsyncSocketManuallyEvaluateTrust];
    
        [_gcdAsyncTCPSocket startTLS:settings];
    }
    

    When you set GCDAsyncSocketManuallyEvaluateTrust it gives a call to delegate method

    - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
    

    In which I have evaluate the server's self signed certificate with the local certificate. If its equal then completionHandler(YES) otherwise completionHandler(NO) as follows:

    - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler {
    NSLog(@"didReceiveTrust");
    
    //server certificate
    SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(trust, 0);
    CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate);
    
    const UInt8* const serverData = CFDataGetBytePtr(serverCertificateData);
    const CFIndex serverDataSize = CFDataGetLength(serverCertificateData);
    NSData* cert1 = [NSData dataWithBytes:serverData length:(NSUInteger)serverDataSize];
    
    
    //local certificate
    NSString *localCertFilePath = [[NSBundle mainBundle] pathForResource:@"LocalCertificate" ofType:@"cer"];
    NSData *localCertData = [NSData dataWithContentsOfFile:localCertFilePath];
    CFDataRef myCertData = (__bridge CFDataRef)localCertData;
    
    
    const UInt8* const localData = CFDataGetBytePtr(myCertData);
    const CFIndex localDataSize = CFDataGetLength(myCertData);
    NSData* cert2 = [NSData dataWithBytes:localData length:(NSUInteger)localDataSize];
    
    
    if (cert1 == nil || cert2 == nil) {
        NSLog(@"Certificate NULL");
        completionHandler(NO);
        return;
    }
    
    
    const BOOL equal = [cert1 isEqualToData:cert2];
    
    if (equal) {
    
        NSLog(@"Certificate match");
        completionHandler(YES);
    
    }else{
    
        NSLog(@"Certificate not match");
        completionHandler(NO);
    }
    }
    

    If certificates matches completionHandler(YES) it will call delegate

    - (void)socketDidSecure:(GCDAsyncSocket *)sock {
    
    NSLog(@"socketDidSecure");
    
    _uploadSocket = sock;
    
    //Do your stuff after this
    }
    

    Hope it will help someone to deal with SSL and GCDAsyncSocket. Happy coding!!