【问题标题】:Swift-NIO secured websocket serverSwift-NIO 安全的 websocket 服务器
【发布时间】:2019-12-19 07:24:23
【问题描述】:

我正在尝试在我的 iOS 应用程序中创建 websocket 服务器和客户端,我在此处的示例实现的帮助下成功地做到了这一点。 (https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer) - 所以目前的工作情况是,我在应用程序启动时运行 websocket 服务器,然后将客户端加载到可以连接到它的 web 视图中。

现在我的问题是我希望我的服务器保护 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 方法。我知道答案就在那里......但我似乎无法确定它。

任何指针都将不胜感激。

谢谢。

【问题讨论】:

    标签: ios swift websocket server swift-nio


    【解决方案1】:

    我需要一个不使用SecIdentityNIOTransportServices 的安全网络套接字,因此根据@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()
    

    【讨论】:

      【解决方案2】:

      要将简单的NIO 服务器转换为NIOTransportServices 服务器,您需要进行以下更改:

      1. NIOTransportServices 的依赖项添加到您的服务器。
      2. MultiThreadedEventLoopGroup 更改为NIOTSEventLoopGroup
      3. ClientBootstrap 更改为NIOTSConnectionBootstrap
      4. ServerBootstrap 更改为NIOTSListenerBootstrap
      5. 构建并运行您的代码。

      一些ChannelOptions 在NIOTransportServices 中不起作用,但大多数都可以:确认事情正常运行的最简单方法是快速测试通用流程。

      这不会为您的应用程序添加任何额外的功能,但它确实为您提供了使用 iOS API 的相同功能。

      要将 TLS 添加到 NIOTSConnectionBootstrapNIOTSListenerBootstrap,请使用 .tlsOptions 函数。例如:

      NIOTSListenerBootstrap(group: group)
          .tlsOptions(myTLSOptions())
      

      配置NWProtocolTLS.Options 是一件有点棘手的事情。您需要获取一个SecIdentity,这需要与钥匙串进行交互。 Quinn 对此进行了一些讨论here

      一旦你有了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,但是没有用。
      • 目前停留在SSL_ERROR_SSL(1): operation failed within the library + SSL routines:OPENSSL_internal:NO_CERTIFICATE_SET:。 -- 将尝试生成自签名证书 - 但如果我可以避免在验证过程中消耗 CPU 周期,因为我不需要它。
      • @Lukasa:你有没有机会提供一个使用 swift-nio-ssl 而不是 NIOTransportServices 的例子?我花了很长时间弄清楚正确的步骤是什么。
      • 嘿布莱恩,你的答案似乎包含正确的例子。
      猜你喜欢
      • 2018-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-05
      • 1970-01-01
      • 2015-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多