Как преобразовать NSStream (NSInputStream / NSOutputStream) в SSL после открытия потока?

У меня есть NSInputStream и NSOutputStream из этого кода

    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()
    }

Проблема: как преобразовать этот поток в SSL после его открытия?

Я пробовал выполнить следующее, но получаю NSOSStatusErrorDomain:

Операция не может быть завершена. (Ошибка OSStatus -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)

}

person Adrian    schedule 28.12.2016    source источник


Ответы (2)


Настройка потоков сокетов

Как вы знаете, NSStream не изначально поддерживает подключение к удаленному хосту в iOS; вы создаете экземпляры CFStream с CFStreamCreatePairWithSocketToHost, затем подключаетесь к NSStream. Код правильный.

Кроме того, вы не конвертируете поток в ssl после его открытия; вы устанавливаете его свойства и сначала настраиваете соединение, а затем открываете его.

Для безопасности SSL NSStream определяет различные свойства уровня [...]

Вы должны установить свойство перед открытием потока. Как только он открывается, он проходит через протокол рукопожатия, чтобы узнать, какой уровень безопасности SSL использует другая сторона соединения.

Защита и настройка соединения

Прежде чем открыть объект потока, вы можете настроить безопасность и другие функции для подключения к удаленному хосту (Руководство по потоковому программированию).

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()
}
person SwiftArchitect    schedule 03.01.2017
comment
Привет, спасибо за ваш ответ. Но моя программа будет работать как сервер. Таким образом, использование CFStreamCreatePairWithSocketToHost не будет применимо, не так ли? Потому что программа будет получать соединение, а не устанавливать соединение. - person Adrian; 05.01.2017
comment
Также мне нужно открыть соединение, потому что мой клиент сначала отправит подтверждение перед отправкой SSL Hello Packet. Однажды я получил эти подтверждения. Мне нужно изменить поток для обработки приветственного пакета SSL. Сначала я попытался inputStream.close() и повторно открыть его после установки SSL. Но все равно показывает ту же ошибку Операция не может быть завершена. (Ошибка OSStatus -9801). Так вы думаете, что мы не можем сделать это? Должен ли я просто дать новый порт клиенту, чтобы клиент мог подключиться к новому потоку, для которого я установлю настройку SSL перед его открытием. - person Adrian; 05.01.2017
comment
Я понимаю. Документация iOS непреклонна по этому поводу: нельзя изменить NSStream после его открытия. Я интерпретирую это так: поток — это канал, по которому будет взаимодействовать уровень сокетов; немного похоже на открытый файл. Рассматривали ли вы возможность перенаправления трафика на новый сокет после установления соединения? - person SwiftArchitect; 05.01.2017

Это ошибка для errSSLNegotiation в SecureTransport.h в Security фреймворке.

-9801 The cipher suite negotiation failed.

Возможно, сервер, к которому вы пытаетесь подключиться, имеет очень старую реализацию SSL или конфигурация вашего набора шифров сервера не соответствует конфигурации телефона.

Источник

person Alistra    schedule 01.01.2017