How to convert NSStream (NSInputStream / NSOutputStream) to SSL after stream opened?
Asked Answered
H

2

6

I have NSInputStream and NSOutputStream from this code

    var readStream: Unmanaged<CFReadStream>?
    var writeStream: Unmanaged<CFWriteStream>?

    CFStreamCreatePairWithSocket(kCFAllocatorDefault, sslSocket!, &readStream, &writeStream)
    if readStream != nil && writeStream != nil {
        CFReadStreamSetProperty(readStream!.takeUnretainedValue(), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue)
        CFWriteStreamSetProperty(writeStream!.takeUnretainedValue(), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue)

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

        // 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


        // 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)

        inputStream!.open()
        outputStream!.open()
    }

Issue: How do I convert those stream to SSL after it was opened ?

I tried following, but I get NSOSStatusErrorDomain:

The operation couldn't be completed. (OSStatus error -9801)

if (sslEnable) {
    let sslSettings = [
        NSString(format: kCFStreamSSLValidatesCertificateChain): kCFBooleanFalse,
        NSString(format: kCFStreamSSLPeerName): kCFNull,
        NSString(format: kCFStreamSSLIsServer): kCFBooleanTrue,
    ]
    CFReadStreamSetProperty(inputStream, kCFStreamPropertySSLSettings, sslSettings)
    CFWriteStreamSetProperty(outputStream, kCFStreamPropertySSLSettings, sslSettings)

}
Henrique answered 28/12, 2016 at 7:6 Comment(0)
S
2

Setting Up Socket Streams

As you know, NSStream does not support connecting to a remote host on iOS natively ; you create instances of CFStream with CFStreamCreatePairWithSocketToHost, then bridge over to NSStream. The code is correct.

Furthermore, you do not convert a stream to ssl after it is opened ; you set its properties and configure the connection first, then open it.

For SSL security, NSStream defines various security-level properties [...]

You must set the property before you open the stream. Once it opens, it goes through a handshake protocol to find out what level of SSL security the other side of the connection is using.

Securing and Configuring the Connection

Before you open a stream object, you might want to set security and other features for the connection to the remote host (Stream Programming Guide).

if let inputStream = inputStream, let outputStream = outputStream {
    inputStream.schedule(in: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
    outputStream.schedule(in: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)

    if (sslEnable) {
        inputStream.setProperty(StreamSocketSecurityLevel.tlSv1,
                               forKey: Stream.PropertyKey.socketSecurityLevelKey)
        outputStream.setProperty(StreamSocketSecurityLevel.tlSv1,
                                 forKey: Stream.PropertyKey.socketSecurityLevelKey)

        let sslSettings = [
            NSString(format: kCFStreamSSLValidatesCertificateChain): kCFBooleanFalse,
            NSString(format: kCFStreamSSLPeerName): kCFNull,
            NSString(format: kCFStreamSSLIsServer): kCFBooleanTrue,
            ] as [NSString : Any]

        inputStream.setProperty(sslSettings,
                                forKey: Stream.PropertyKey(rawValue:
                                    kCFStreamPropertySSLSettings as String))
        outputStream.setProperty(sslSettings,
                                 forKey: Stream.PropertyKey(rawValue:
                                    kCFStreamPropertySSLSettings as String))

    }
    inputStream.open()
    outputStream.open()
}
Scarborough answered 3/1, 2017 at 2:59 Comment(3)
Hi, thanks for your reply. But my program will work as server. So using CFStreamCreatePairWithSocketToHost won't be applicable isn't it ? Cause the program will receiving the connection, not making a connection.Henrique
Also I need to open my connection cause my client will send confirmation first before sending SSL Hello Packet . Once I received those confirmation. I need to change the stream to handle SSL hello packet. I did try to inputStream.close() first and re open it after setting the SSL. But it still showing the same error "The operation couldn't be completed. (OSStatus error -9801)". So do you think we cannot do that ? Should I just give a new port to client so client could connect to new stream which I will set the SSL setting before opening it.Henrique
I see. The iOS documentation is adamant about this: can't change an NSStream once it is opened. One way I interpret this is that a stream is a channel through which the socket layer will communicate ; a bit like an opened file. Have you considered redirecting traffic to a new socket once you made the connection?Scarborough
M
1

This is the error for errSSLNegotiation in SecureTransport.h in the Security framework

-9801 The cipher suite negotiation failed.

Maybe the server that you are trying to connect to has a really old SSL implementation or your server cipher suite configuration doesn't match the phone's configuration.

Source

Moniliform answered 1/1, 2017 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.