42

I'm trying to connect an iOS app to a Windows C# sever using TLS over TCP/IP.

The TLS connection is using untrusted certificates created from an untrusted CA root certificate using the makecert utility.

To test these certificates I created a simple C# client and using those certificates it was able to connect and communicate with the server.

I'm not skilled at iOS development, but I did manage to find some code that connects me to the server, as follows:

-(bool)CreateAndConnect:(NSString *) remoteHost withPort:(NSInteger) serverPort
{
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(remoteHost),
                                       serverPort, &readStream, &writeStream);

    CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel,
                            kCFStreamSocketSecurityLevelNegotiatedSSL);

    NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
    NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;

    [inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];

    // load certificate from servers exported p12 file
    NSArray *certificates = [[NSArray alloc] init];
    [self loadClientCertificates:certificates];

    NSDictionary *sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                 (id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
                                 certificates,(id)kCFStreamSSLCertificates,
                                 nil];

    [inputStream setProperty:sslSettings forKey:(__bridge NSString *)kCFStreamPropertySSLSettings];

    [inputStream setDelegate:self];
    [outputStream setDelegate:self];

    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    CFReadStreamOpen(readStream);
    CFWriteStreamOpen(writeStream);

    return true;
}

The code also seems to do some form of TLS negotiation, as the C# server rejects the connection if the p12 certificates are not provided as part of the NSStream settings.

So it appears like the first stage of the TLS negotiation is working.

To validate the server certificate I have this function, which gets called by the NSStream delegate on the NSStreamEventHasSpaceAvailable event:

// return YES if certificate verification is successful, otherwise NO
-(BOOL) VerifyCertificate:(NSStream *)stream
{
    NSData *trustedCertData = nil;
    BOOL result             = NO;
    SecTrustRef trustRef    = NULL;
    NSString *root_certificate_name      = @"reference_cert";
    NSString *root_certificate_extension = @"der";

    /* Load reference cetificate */
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    trustedCertData = [NSData dataWithContentsOfFile:[bundle pathForResource: root_certificate_name ofType: root_certificate_extension]];

    /* get trust object */
    /* !!!!! error is here as trustRef is NULL !!!! */
    trustRef = (__bridge SecTrustRef)[stream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];

    /* loacate the reference certificate */
    NSInteger numCerts = SecTrustGetCertificateCount(trustRef);
    for (NSInteger i = 0; i < numCerts; i++) {
        SecCertificateRef secCertRef = SecTrustGetCertificateAtIndex(trustRef, i);
        NSData *certData = CFBridgingRelease(SecCertificateCopyData(secCertRef));
        if ([trustedCertData isEqualToData: certData]) {
            result = YES;
            break;
        }
    }
    return result;
}

Now the problem is, no matter what I try, the trustRef object is always null.

From this Apple developer link: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html

There is this quote that suggest this should not be the case:

By the time your stream delegate’s event handler gets called to indicate that there is space available on the socket, the operating system has already constructed a TLS channel, obtained a certificate chain from the other end of the connection, and created a trust object to evaluate it.

Any hints on how to fix this?

How can I get access to that trustRef object for the NSStream?

Edit:

Thanks for the reply 100phole.

In trying to get this to work, I thought this might have something to do with the issue and in one of my many attempts I moved all of those socket related items into a class:

Something like this:

@interface Socket
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    NSInputStream *inputStream;
    NSOutputStream *outputStream;
@end

But that came up with the same results :(

I only reverted back to the version shown above because, based on my Google searching, that appears to be a fairly code common pattern.

For example, even this code from the Apple Developer site uses a very similar style:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Streams/Articles/NetworkStreams.html#//apple_ref/doc/uid/20002277-BCIDFCDI

As I mentioned earlier, I'm no expert in Objective-C (far from it), so I might be wrong, but from what I have seen, moving those items into a class and having them persist did not seem to make any difference.

9
  • 2
    Your streams appear to be local variables in a method, which means they will be destroyed when the method returns.
    – l00phole
    Oct 16, 2015 at 9:29
  • Looked at your code only long enough to ask... why not use the builtin NSURL session and connection classes? The connection setup and auth handshakes are all handled for you (with hooks to interject when necessary). Nov 8, 2015 at 23:27
  • You see the note in the web page you reference saying /* Store a reference to the input and output streams so that they don't go away.... */
    – Wain
    Nov 9, 2015 at 17:12
  • 1
    What version of iOS are you testing on?
    – Nicolas S
    Nov 10, 2015 at 18:22
  • 2
    If you're connecting at a low level, this might not be in effect, but iOS 9 uses Application Transport Security, which you might want to disable or white list your server due to TLS. I would try to disable it altogether just to make sure it is not bothering you. stackoverflow.com/questions/32892121/…
    – Nicolas S
    Nov 13, 2015 at 15:04

2 Answers 2

0

Since there seems to be some interest in this question I decided to update the question with a answer with details on how this problem was eventually solved.

First some background. I inherited this code from a previous developer and my role was to get the broken code to work.

I spent a lot of time writing and re-writing the connection code using the details from the Apple iOS developer web page, but nothing seemed to work.

I finally decided to take a closer look at this function, code I had inherited and incorrectly assumed was working:

[self loadClientCertificates:certificates];

At first glance the code looked OK. The function did nothing more than load certificates from file. But on closer inspection, while the code was loading the certificates correctly, it was not returning those certificates to the caller!!!

After fixing that code so that it correctly returned the certificates the connection code worked fine and the SecTrustRef was no longer NULL.

In summary:

1) The Apple documentation, while lacking good examples does appear to be accurate.

2) The reason the SecTrustRef was NULL was because no valid certificate could be found for the connection negotiations phase and that was because no certificates where being made available to the connection API due to the earlier mentioned coding error.

3) If you are seeing a similar error, my suggestion would be to check and double check your code, because as would be expected, the iOS side of the equation works as documented.

0

I recently created an Obj-C package to handle TLS taking into account the latest restrictions imposed by Apple. Getting the certificates right is a very important step.

https://github.com/eamonwhiter73/IOSObjCWebSockets

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.