Snapper26 Snapper26 - 1 month ago 49
iOS Question

iOS SSL Connection in Swift

I am trying to establish a simple socket connection (NO HTTP) from my iOS app to my backend server (Node.js). The servers certificate has been created and signed using a custom CA that I made myself. I believe that in order to get iOS to trust my server I will have to somehow add this custom CA Certificate to the list of trusted certificates that are used to determine trust sort of how a TrustStore in Java/Android works.

I have tried to connect using the code below and there are no errors however the write() function does not seem to succeed.

Main View Controller:

override func viewDidLoad() {
// Do any additional setup after loading the view, typically from a nib.

let api: APIClient = APIClient()

api.initialiseSSL("", port: 8080)




APIClient class

class APIClient: NSObject, NSStreamDelegate {

var readStream: Unmanaged<CFReadStreamRef>?
var writeStream: Unmanaged<CFWriteStreamRef>?

var inputStream: NSInputStream?
var outputStream: NSOutputStream?

func initialiseSSL(host: String, port: UInt32) {
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream)

inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()

inputStream?.delegate = self
outputStream?.delegate = self

inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

let cert: SecCertificateRef? = CreateCertificateFromFile("ca", ext: "der")

if cert != nil {

let certs: NSArray = NSArray(objects: cert!)

let sslSettings = [
NSString(format: kCFStreamSSLLevel): kCFStreamSocketSecurityLevelNegotiatedSSL,
NSString(format: kCFStreamSSLValidatesCertificateChain): kCFBooleanFalse,
NSString(format: kCFStreamSSLPeerName): kCFNull,
NSString(format: kCFStreamSSLCertificates): certs,
NSString(format: kCFStreamSSLIsServer): kCFBooleanFalse

CFReadStreamSetProperty(inputStream, kCFStreamPropertySSLSettings, sslSettings)
CFWriteStreamSetProperty(outputStream, kCFStreamPropertySSLSettings, sslSettings)


func write(text: String) {
let data = [UInt8](text.utf8)

outputStream?.write(data, maxLength: data.count)

func CreateCertificateFromFile(filename: String, ext: String) -> SecCertificateRef? {
var cert: SecCertificateRef!

if let path = NSBundle.mainBundle().pathForResource(filename, ofType: ext) {

let data = NSData(contentsOfFile: path)!

cert = SecCertificateCreateWithData(kCFAllocatorDefault, data)!
else {


return cert

func deinitialise() {


I understand how SSL/TLS works and all since I have done this all fine in the Android version of this same app. I am just confused with the iOS implementation of SSL.

I am from a Java background and have been going with this problem for 3 weeks. Any help would be appreciated.

Prefer answers in Swift code, not Objective C but if you only have Obj C thats ok too :)


Ok I spent 8 weeks on this issue :( but i finally managed to put together a working solution. I must say that SSL/TLS on iOS is a joke. Java on Android leaves it for dead. It is completely ridiculous that in order to evaluate trust for a self signed certificate, you must disable certificate chain verification completely and do it yourself. Completely ridiculous. Anyway this is the fully working solution that connects to a remote socket server (no HTTP) using a self signed server certificate. Feel free to edit this answer to provide a better answer since I haven't had the change to add code for sending and receiving data yet :)

//  ProXimityAPIClient.swift
//  SecureSocket
//  Created by snapper26 on 2/9/16.
//  Copyright © 2016 snapper26. All rights reserved.

    import Foundation

    class ProXimityAPIClient: NSObject, NSStreamDelegate {

    // Input and output streams for socket
    var inputStream: NSInputStream?
    var outputStream: NSOutputStream?

    // Secondary delegate reference to prevent ARC deallocating the NSStreamDelegate
    var inputDelegate: NSStreamDelegate?
    var outputDelegate: NSStreamDelegate?

    // Add a trusted root CA to out SecTrust object
    func addAnchorToTrust(trust: SecTrust, certificate: SecCertificate) -> SecTrust {
        let array: NSMutableArray = NSMutableArray()


        SecTrustSetAnchorCertificates(trust, array)

        return trust

    // Create a SecCertificate object from a DER formatted certificate file
    func createCertificateFromFile(filename: String, ext: String) -> SecCertificate {
        let rootCertPath = NSBundle.mainBundle().pathForResource(filename, ofType: ext)

        let rootCertData = NSData(contentsOfFile: rootCertPath!)

        return SecCertificateCreateWithData(kCFAllocatorDefault, rootCertData!)!

    // Connect to remote host/server
    func connect(host: String, port: Int) {
        // Specify host and port number. Get reference to newly created socket streams both in and out
        NSStream.getStreamsToHostWithName(host, port: port, inputStream: &inputStream, outputStream: &outputStream)

        // Create strong delegate reference to stop ARC deallocating the object
        inputDelegate = self
        outputDelegate = self

        // Now that we have a strong reference, assign the object to the stream delegates
        inputStream!.delegate = inputDelegate
        outputStream!.delegate = outputDelegate

        // This doesn't work because of arc memory management. Thats why another strong reference above is needed.
        //inputStream!.delegate = self
        //outputStream!.delegate = self

        // Schedule our run loops. This is needed so that we can recieve NSStreamEvents
        inputStream!.scheduleInRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)
        outputStream!.scheduleInRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)

        // Enable SSL/TLS on the streams
        inputStream!.setProperty(kCFStreamSocketSecurityLevelNegotiatedSSL, forKey: kCFStreamPropertySocketSecurityLevel as String)
        outputStream!.setProperty(kCFStreamSocketSecurityLevelNegotiatedSSL, forKey: kCFStreamPropertySocketSecurityLevel as String)

        // Defin custom SSL/TLS settings
        let sslSettings = [
            // NSStream automatically sets up the socket, the streams and creates a trust object and evaulates it before you even get a chance to check the trust yourself. Only proper SSL certificates will work with this method. If you have a self signed certificate like I do, you need to disable the trust check here and evaulate the trust against your custom root CA yourself.
            NSString(format: kCFStreamSSLValidatesCertificateChain): kCFBooleanFalse,
            NSString(format: kCFStreamSSLPeerName): kCFNull,
            // We are an SSL/TLS client, not a server
            NSString(format: kCFStreamSSLIsServer): kCFBooleanFalse

        // Set the SSL/TLS settingson the streams
        inputStream!.setProperty(sslSettings, forKey: kCFStreamPropertySSLSettings as String)
        outputStream!.setProperty(sslSettings, forKey: kCFStreamPropertySSLSettings as String)

        // Open the streams

    // This is where we get all our events (haven't finished writing this class)
    func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
        switch eventCode {
        case NSStreamEvent.None:
        case NSStreamEvent.EndEncountered:
            print("End Encountered")
        case NSStreamEvent.OpenCompleted:
            print("Open Completed")
        case NSStreamEvent.HasSpaceAvailable:
            print("Has Space Available")

            // If you try and obtain the trust object (aka kCFStreamPropertySSLPeerTrust) before the stream is available for writing I found that the oject is always nil!
            var sslTrustInput: SecTrust? = inputStream!.propertyForKey(kCFStreamPropertySSLPeerTrust as String) as! SecTrust
            var sslTrustOutput: SecTrust? = outputStream!.propertyForKey(kCFStreamPropertySSLPeerTrust as String) as! SecTrust

            if (sslTrustInput == nil) {
                print("INPUT TRUST NIL")
            else {
                print("INPUT TRUST NOT NIL")

            if (sslTrustOutput == nil) {
                print("OUTPUT TRUST NIL")
            else {
                print("OUTPUT TRUST NOT NIL")

            // Get our certificate reference. Make sure to add your root certificate file into your project.
            let rootCert: SecCertificate? = createCertificateFromFile("ca", ext: "der")

            // TODO: Don't want to keep adding the certificate every time???
            // Make sure to add your trusted root CA to the list of trusted anchors otherwise trust evaulation will fail
            sslTrustInput = addAnchorToTrust(sslTrustInput!, certificate: rootCert!)
            sslTrustOutput = addAnchorToTrust(sslTrustOutput!, certificate: rootCert!)

            // convert kSecTrustResultUnspecified type to SecTrustResultType for comparison
            var result: SecTrustResultType = SecTrustResultType(kSecTrustResultUnspecified)

            // This is it! Evaulate the trust.
            let error: OSStatus = SecTrustEvaluate(sslTrustInput!, &result)

            // An error occured evaluating the trust check the OSStatus codes for Apple at
            if (error != noErr) {
                print("Evaluation Failed")

            if (result != SecTrustResultType(kSecTrustResultProceed) && result != SecTrustResultType(kSecTrustResultUnspecified)) {
                // Trust failed. This will happen if you faile to add the trusted anchor as mentioned above
                print("Peer is not trusted :(")
            else {
                // Peer certificate is trusted. Now we can send data. Woohoo!
                print("Peer is trusted :)")

        case NSStreamEvent.HasBytesAvailable:
            print("Has Bytes Available")
        case NSStreamEvent.ErrorOccurred:
            print("Error Occured")