Objective-C 和 Swift 的 Apple TLS 之间的区别

Difference between Apple TLS with Objective-C and Swift

我正在使用 Apple 的 CFNetworking 获取 TLS 流。我在将 Objective-C 代码移植到 Swift 时遇到了一些麻烦。

使用完全相同的步骤,它在使用 Objective-C 时有效,但在尝试使用 Swift 时握手始终失败。

工作目标-c

- (void)connect()
{
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
                                       (__bridge CFStringRef)hostAddress,
                                       port,
                                       &readStream,
                                       &writeStream);

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

    [self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
                      forKey:NSStreamSocketSecurityLevelKey];
    [self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
                       forKey:NSStreamSocketSecurityLevelKey];

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

    [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                                forMode:NSDefaultRunLoopMode];

    [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                                 forMode:NSDefaultRunLoopMode];

    [self.inputStream open];
    [self.outputStream open];
}

不工作Swift

func connect() {
    var readStream: Unmanaged<CFReadStream>?
    var writeStream: Unmanaged<CFWriteStream>?

    let host = "some_host"
    let hostAsCFString = host as NSString
    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
                                       hostAsCFString,
                                       1337,
                                       &readStream,
                                       &writeStream)

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

    inputStream!.delegate = self
    outputStream!.delegate = self

    inputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL,
                             forKey: NSStreamSocketSecurityLevelKey)
    outputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL,
                              forKey: NSStreamSocketSecurityLevelKey)

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

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

两者都试图连接到相同的服务器和相同的端口。

wireshark 的屏幕截图:

工作对象 C

不工作 Swift

我对发生的事情一无所知。我不知道为什么 Obj-C 版本使用 TLS v1.2 启动 Client Hello,但是 Swift 尝试使用 TLS v1.0,然后就放弃了。不知道为什么 Swift 版本需要这么长时间才能发送 Client Hello,之前会发送 Keepalive 数据包?任何帮助将不胜感激。

原来没有什么区别,我就是个白痴。一旦为输入和输出流调用了 NSStreamEvent.OpenCompleted 事件,我立即调用 outputStream.write()。它正在写入 SSL 握手的缓冲区,并把它搞得一团糟。

在我为 Obj-c 和 Swift 创建一个 MVP 之前找不到,这只是表明如果你花时间创建一个有效的 MVP,你可能会在编写时弄清楚它。现在,如果我能想办法在握手完成后得到通知,这个问题就永远可以避免了。