swiftxcodemacossslnsstream

How to use a self generated ssl cert with NSStream(s) in Swift


I have a python server in the cloud that accepts socket requests. I have a Swift (xCode) client application that uses NS(in/out)putstreams to communicate with the python server app.

I generated my own SSL cert and key files on the server.

With the SSL wrapping code commented out on my server side, I can get my client and server to talk just fine. With SSL I get handshake errors.

Both the server and client are using TLSV1 for security level protocol.

On the mac side I have my server cert file installed and allowed in my user keychain, but i'm thinking that the application does not look there for trusted certs.

Is there a way to specify a cert file, for the NSStream(s) to use, directly in the xCode application pragmatically. Or a way to install the cert on the app?

I know in python when you wrap a socket with SSL you can tell it it's a client and to use a specific cert file from a path. Is there a MacOS Swift equivalent?

Or am I going about this all wrong?

Below is my "Light Socket" Swift class. Supper simple but works when not using the SSL at both ends.

class SSLSocketLite {

    // The input stream.
    private var inStream: NSInputStream?
    // The output stream.
    private var outStream: NSOutputStream?
    // The host to connect to.
    private var host: String
    // The port to connect on.
    private var port: Int

    init(inHost:String, inPort:Int) {
        host = inHost
        port = inPort
        NSStream.getStreamsToHostWithName(host, port: port, inputStream: &inStream, outputStream: &outStream)
    }

    func Open() {
        inStream?.open()
        outStream?.open()

        inStream?.setProperty(NSStreamSocketSecurityLevelTLSv1, forKey: NSStreamSocketSecurityLevelKey)
        outStream?.setProperty(NSStreamSocketSecurityLevelTLSv1, forKey: NSStreamSocketSecurityLevelKey)
    }

    func Read() -> String! {
        var buffer = Array<UInt8>(count:1024, repeatedValue: 0)
        if inStream!.hasBytesAvailable {
            inStream!.read(&buffer, maxLength: 1024)
            let responseString = NSString(bytes: buffer, length: buffer.count, encoding: NSUTF8StringEncoding) as! String
            return responseString
        }
        return nil
    }

    func Write(msg:String) {
        let data:NSData = msg.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
        outStream!.write(UnsafePointer(data.bytes), maxLength: data.length)
    }

    func Close() {
        inStream?.close()
        outStream?.close()
    }
}

I gathered all of this from a bunch of other posts, but could not find any mention of where and when a cert file is actually used by the Swift calls to communicate out to the server.

If there is a key element to this that I am either missing or misunderstanding, please let me know! And thanks in advance!


Solution

  • I found my answer. I was missing these lines in my open method.

    they are required to automate the handshake for SSL.

    inStream?.scheduleInRunLoop(.mainRunLoop(), forMode: NSDefaultRunLoopMode)
    outStream?.scheduleInRunLoop(.mainRunLoop(), forMode: NSDefaultRunLoopMode)
    

    My other questions still could use answering though.

    Thanks!