You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在macOS Swift应用中通过NWConnection实现带证书的TLS/SSL握手

基于NWConnection实现TLS加密通信

针对你在macOS应用中通过TCP连接后触发TLS握手的需求,以下是具体实现步骤和代码示例:

1. 准备服务器证书

将服务器的CA证书或自签证书(格式如.cer/.pem)添加到Xcode项目中,确保在Target的Build Phases > Copy Bundle Resources里勾选该证书,保证应用能读取到文件。

2. 加载证书并配置TLS选项

首先实现证书加载逻辑,然后自定义TLS信任评估策略——因为是局域网自签证书,系统默认的信任链验证会失败,所以需要指定只信任我们提供的服务器证书:

// 加载服务器证书
private func loadServerCertificate() -> SecCertificate? {
    guard let certPath = Bundle.main.path(forResource: "server", ofType: "cer"),
          let certData = try? Data(contentsOf: URL(fileURLWithPath: certPath)) else {
        return nil
    }
    return SecCertificateCreateWithData(nil, certData as CFData)
}

// 配置TLS选项
private func configureTLSOptions() -> NWProtocolTLS.Options {
    let tlsOptions = NWProtocolTLS.Options()
    
    guard let serverCert = loadServerCertificate() else {
        fatalError("无法加载服务器证书,请检查文件路径")
    }
    
    // 设置自定义信任验证逻辑
    sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { (_, secTrust, completion) in
        guard let trust = secTrust else {
            completion(false)
            return
        }
        
        var trustResult = SecTrustResultType.invalid
        // 创建SSL策略,仅验证服务器证书
        let policies = [SecPolicyCreateSSL(true, nil)]
        SecTrustSetPolicies(trust, policies as CFArray)
        // 将我们的服务器证书设为唯一信任锚点
        SecTrustSetAnchorCertificates(trust, [serverCert] as CFArray)
        SecTrustSetAnchorCertificatesOnly(trust, true)
        
        // 执行信任评估
        let status = SecTrustEvaluate(trust, &trustResult)
        completion(status == errSecSuccess && (trustResult == .proceed || trustResult == .unspecified))
    }, .global())
    
    return tlsOptions
}

3. 修改连接流程:TCP连接后升级TLS

原TCP连接建立后,监听服务器发送的"TLS"触发消息,收到后切换到TLS加密模式:

class ConnectionManager {
    private var connection: NWConnection?
    private var isTLSActive = false
    private let tlsOptions = configureTLSOptions()
    
    // 启动TCP连接
    func startTCPConnection(host: String, port: UInt16) {
        guard let port = NWEndpoint.Port(rawValue: port) else {
            print("端口无效")
            return
        }
        
        connection = NWConnection(host: NWEndpoint.Host(host), port: port, using: .tcp)
        
        connection?.stateUpdateHandler = { [weak self] state in
            switch state {
            case .ready:
                print("TCP连接成功,等待TLS触发消息")
                self?.startReceivingData()
            case .failed(let error):
                print("TCP连接失败: \(error.localizedDescription)")
            case .cancelled:
                print("连接已取消")
            default: break
            }
        }
        
        connection?.start(queue: .global())
    }
    
    // 监听数据接收
    private func startReceivingData() {
        connection?.receive(minimumIncompleteLength: 1, maximumLength: 1024) { [weak self] data, _, isComplete, error in
            guard let self = self else { return }
            
            if let error = error {
                print("接收数据错误: \(error.localizedDescription)")
                return
            }
            
            if let data = data, !data.isEmpty {
                guard let message = String(data: data, encoding: .utf8) else {
                    print("无法解析收到的消息")
                    return
                }
                
                if !self.isTLSActive && message.contains("TLS") {
                    // 触发TLS握手,切换到加密连接
                    self.switchToTLSConnection()
                } else if self.isTLSActive {
                    // 处理加密后的业务数据
                    self.processEncryptedData(data: data)
                }
            }
            
            if !isComplete {
                self.startReceivingData()
            }
        }
    }
    
    // 切换到TLS加密连接
    private func switchToTLSConnection() {
        guard let connection = connection else { return }
        
        let tlsProtocol = NWProtocolTLS.definition(options: tlsOptions)
        
        // 应用协议变更,发起TLS握手
        connection.applyChanges { changes in
            changes.renegotiate(tlsProtocol)
        } completionHandler: { [weak self] error in
            if let error = error {
                print("TLS握手失败: \(error.localizedDescription)")
            } else {
                print("TLS握手成功,已进入加密通信模式")
                self?.isTLSActive = true
                // 继续监听加密数据
                self?.startReceivingData()
            }
        }
    }
    
    // 处理加密业务数据
    private func processEncryptedData(data: Data) {
        print("收到加密数据: \(data)")
        // 示例:发送加密响应
        let responseData = "加密响应内容".data(using: .utf8)!
        self.sendEncryptedData(data: responseData)
    }
    
    // 发送加密数据
    private func sendEncryptedData(data: Data) {
        guard isTLSActive, let connection = connection else { return }
        
        connection.send(content: data, completion: .contentProcessed { error in
            if let error = error {
                print("发送加密数据失败: \(error.localizedDescription)")
            } else {
                print("加密数据发送成功")
            }
        })
    }
}

4. 使用方式

在需要连接的地方调用:

let manager = ConnectionManager()
manager.startTCPConnection(host: "10.1.xxx.xx", port: 1515)

关键说明

  • 信任评估:自定义验证逻辑确保只信任指定的服务器证书,避免局域网自签证书被系统拒绝
  • 协议切换:通过applyChanges和renegotiate方法实现TCP到TLS的无缝升级,无需重新建立连接
  • 状态区分:用isTLSActive标记加密状态,确保触发消息和业务数据被正确处理

内容的提问来源于stack exchange,提问作者Luu Minh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 08:48:09