Swift-NIO 安全的 WebSocket 服务器

4
我在尝试在我的iOS应用中创建websocket服务器和客户端,通过参考这里的示例实现成功。(https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer)- 现在的工作情况是,在应用启动时运行websocket服务器,然后在webview中加载客户端并连接它。

现在我的问题是我想让我的服务器成为安全的websocket服务器(基本上从HTTPS html页面连接到websocket服务器)。

我对网络编程很陌生,而Swift-nio文档则缺乏。据我所知,我可以使用(https://github.com/apple/swift-nio-transport-services)。

我找到了这个线程,它正是我需要的 - https://github.com/apple/swift-nio-transport-services/issues/39 - 我可以禁用TLS认证,因为在我的用例中我不关心,只要我能够连接websocket即可。

那么我的问题是如何扩展我的客户端(https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketClient)和服务器(https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer)以使用swift-nio-transport-service。

我可以添加NIOSSLContext和其他内容,但我认为我需要添加EventLoopGroup和新的bootstrap方法。答案就在那里...但我似乎无法确定它。

任何指针都将不胜感激。

谢谢。

2个回答

6
要将一个简单的 NIO 服务器转换为 NIOTransportServices,您需要进行以下更改:
  1. 在您的服务器中添加对 NIOTransportServices 的依赖。
  2. MultiThreadedEventLoopGroup 更改为 NIOTSEventLoopGroup
  3. ClientBootstrap 更改为 NIOTSConnectionBootstrap
  4. ServerBootstrap 更改为 NIOTSListenerBootstrap
  5. 构建并运行您的代码。
一些 ChannelOptionNIOTransportServices 中不起作用,但大多数都可以: 确认事情是否正常工作的最简单方法是快速测试常见流程。
这不会向您的应用程序添加任何额外的功能,但它确实使用 iOS API 提供了相同的功能。
要将 TLS 添加到 NIOTSConnectionBootstrapNIOTSListenerBootstrap,请使用 .tlsOptions 函数。例如:
NIOTSListenerBootstrap(group: group)
    .tlsOptions(myTLSOptions())

配置 NWProtocolTLS.Options 是有一定技巧的。您需要获取一个 SecIdentity,这需要与钥匙串交互。Quinn在 这里 讨论了这个问题。

一旦您拥有了一个 SecIdentity,您可以像下面这样使用它:

func myTLSOptions() -> NWProtocolTLS.Options {
    let options = NWProtocolTLS.Options()
    let yourSecIdentity = // you have to implement something here
    sec_protocol_options_set_local_identity(options.securityProtocolOptions, sec_identity_create(yourSecIdentity)
    return options
}

一旦你写好了那段代码,一切都应该顺利进行!


作为扩展,如果你想在Linux上保护一个NIO服务器,可以使用swift-nio-ssl。这有单独的配置,因为钥匙串API不可用,所以你需要从文件中加载更多的密钥和证书。


我按照你的建议使用NIOTransportServices成功地运行了客户端和服务器。现在我正在尝试让SecIdentity工作。所以我猜sec_protocol_options_set_peer_authentication_required(options.securityProtocolOptions, false)仍然无法正常工作?我需要获取自签名证书吗 - 没有其他方法吗?我知道这不是推荐的方式,但对于我的用例,我不需要TLS验证。我还尝试过.tlsOptions(.init())来删除TLS,但它没有起作用。 - Appu Newbie
目前遇到了 SSL_ERROR_SSL(1): operation failed within the librarySSL routines:OPENSSL_internal:NO_CERTIFICATE_SET: 问题。-- 将尝试生成自签名证书,但如果可以避免在验证过程中消耗 CPU 运算周期,那就更好了,因为我不需要它。 - Appu Newbie
@Lukasa:您能提供使用swift-nio-ssl而不是NIOTransportServices进行此操作的示例吗?我正在努力弄清楚正确的步骤是什么。 - Bryan
嘿,Bryan,你的答案似乎包含了正确的示例。 - Lukasa

0

我需要一个安全的 WebSocket,但不想使用SecIdentity或者NIOTransportServices,基于@Lukasa的提示,我拼凑出了一个看起来可以正常工作的示例,它是基于swift-nio-ssl制作的。

我不知道它是否正确,但我把它放在这里,以防别人有所收益。为简洁起见,错误处理和当try失败时中止都被省略了。

let configuration = TLSConfiguration.forServer(certificateChain: try! NIOSSLCertificate.fromPEMFile("/path/to/your/tlsCert.pem").map { .certificate($0) }, privateKey: .file("/path/to/your/tlsKey.pem"))
let sslContext = try! NIOSSLContext(configuration: configuration)
            
let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture<Void> = { channel, req in
                
    WebSocket.server(on: channel) { ws in

       ws.send("You have connected to WebSocket")

       ws.onText { ws, string in
           print("Received text: \(string)")
       }
                
       ws.onBinary { ws, buffer in
           // We don't accept any Binary data
       }
                
       ws.onClose.whenSuccess { value in
           print("onClose")
       }
   }
}

self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let port: Int = 5759
let promise = self.eventLoopGroup!.next().makePromise(of: String.self)
            
_ = try? ServerBootstrap(group: self.eventLoopGroup!)
                
    // Specify backlog and enable SO_REUSEADDR for the server itself
    .serverChannelOption(ChannelOptions.backlog, value: 256)
    .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
                
    .childChannelInitializer { channel in
                    
        let handler = NIOSSLServerHandler(context: sslContext)
        _ = channel.pipeline.addHandler(handler)
              
        let webSocket = NIOWebSocketServerUpgrader(
            shouldUpgrade: { channel, req in
                return channel.eventLoop.makeSucceededFuture([:])
            },
            upgradePipelineHandler: upgradePipelineHandler
        )
              
        return channel.pipeline.configureHTTPServerPipeline(
            withServerUpgrade: (
                upgraders: [webSocket],
                completionHandler: { ctx in
                     // complete
                })
        )
    }.bind(host: "0.0.0.0", port: port).wait()

_ = try! promise.futureResult.wait()
try! server.close(mode: .all).wait()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接