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

iOS客户端连接本地Mosquitto MQTT Broker时出现TLS版本错误

问题:iOS客户端通过TLS连接Mosquitto MQTT Broker遇SSL版本错误及连接重置

问题现象

客户端日志报错:

[connection] nw_socket_handle_socket_event [C1.1.1.1:1] Socket SO_ERROR [54: Connection reset by peer]

Broker日志报错:

OpenSSL Error[0]: error:1408F10B:SSL routines:ssl3_get_record:wrong version number
Client disconnected: Protocol error.

配置信息

Mosquitto配置(mosquitto.conf)

...
listener 8883
protocol mqtt
allow_anonymous true
require_certificate true
cafile /opt/homebrew/etc/mosquitto/certs/ca.crt
certfile /opt/homebrew/etc/mosquitto/certs/server.crt
keyfile /opt/homebrew/etc/mosquitto/certs/server.key
...

证书创建步骤

openssl genrsa -out ca.key 2048
openssl req -new -x509 -key ca.key -out ca.crt (CN=CA)
openssl genrsa -out server.key 2048
openssl req -new -sha256 -out server.csr -key server.key (CN=localhost)
openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
openssl genrsa -out client.key 2048
openssl req -new -sha256 -out client.csr -key client.key (CN=localhost)
openssl x509 -req -sha256 -days 365 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
openssl pkcs12 -export -out client.p12 -inkey client.key -in client.crt -certfile ca.crt
openssl x509 -inform pem -in ca.crt -outform der -out root.der

iOS客户端代码

证书解析代码

public static func importIdentities(from url: URL, password: String) throws -> [SecIdentity] {
    let p12Data = try Data(contentsOf: url)
    let options: [String: String] = [kSecImportExportPassphrase as String: password]

    var rawItems: CFArray?
    let result = DispatchQueue.global().sync {
        SecPKCS12Import(p12Data as CFData, options as CFDictionary, &rawItems)
    }

    guard result == errSecSuccess else {
        if result == errSecAuthFailed {
            throw Error.invalidPassword
        } else {
            throw Error.invalidData
        }
    }

    guard let items = rawItems as? [[CFString: SecIdentity]] else {
        throw Error.invalidData
    }

    return items.compactMap({ $0[kSecImportItemIdentity] })
}

public static func importCertificate(from url: URL) throws -> SecCertificate {
    let certificateData = try Data(contentsOf: url) as CFData
    guard let certificate = SecCertificateCreateWithData(nil, certificateData) else {
        throw Error.invalidData
    }
    return certificate
}

TLS配置代码

func getNWProtocolTLSOptions() -> NWProtocolTLS.Options {
    let options = NWProtocolTLS.Options()

    sec_protocol_options_set_min_tls_protocol_version(options.securityProtocolOptions, minimumTLSVersion)

    if let maximumTLSVersion = maximumTLSVersion {
        sec_protocol_options_set_max_tls_protocol_version(options.securityProtocolOptions, maximumTLSVersion)
    }

    if let clientIdentity = clientIdentity, let secClientIdentity = sec_identity_create(clientIdentity) {
        sec_protocol_options_set_local_identity(options.securityProtocolOptions, secClientIdentity)
    }

    securityProtocols.forEach {
        sec_protocol_options_add_tls_application_protocol(options.securityProtocolOptions, $0)
    }

    if trustRootCertificates != nil {
        sec_protocol_options_set_verify_block(options.securityProtocolOptions, { _, sec_trust, sec_protocol_verify_complete in
            let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()

            if let trustRootCertificates = trustRootCertificates {
                SecTrustSetAnchorCertificates(trust, trustRootCertificates as CFArray)
            }

            SecTrustEvaluateAsyncWithError(trust, queue) { _, result, error in
                if let error = error {
                    log.error("Trust failed: \(error.localizedDescription)")
                }
                sec_protocol_verify_complete(result)
            }
        }, queue)
    }

    return options
}

可能的原因及排查方向

  • TLS版本不匹配:检查客户端minimumTLSVersion和maximumTLSVersion设置,Mosquitto 2.0.15默认支持TLSv1.2及以上版本。如果客户端设置了过低(如SSLv3)或过高版本,会导致握手失败。建议将客户端最小版本设为tls_protocol_version_tls12,最大版本设为tls_protocol_version_tls13(或不限制最大版本)。同时可以在Mosquitto配置的listener 8883下添加tls_version tlsv1.2,强制Broker使用指定TLS版本。
  • 应用层协议冗余配置:代码中securityProtocols添加的协议可能干扰MQTT的TLS握手。MQTT over TLS不需要额外添加应用层协议(如h2),建议注释掉securityProtocols.forEach这段代码后重试。
  • 客户端证书未正确发送:Mosquitto配置了require_certificate true,需确保客户端在TLS握手时正确传入了clientIdentity。检查importIdentities方法是否成功获取到SecIdentity,以及clientIdentity是否正确传递到TLS配置中。
  • 信任锚配置问题:确认trustRootCertificates是否包含了正确的CA证书(root.der对应的SecCertificate),如果CA证书未正确设置,会导致信任验证失败,触发连接断开。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 07:45:49