Open philipparndt opened 2 years ago
Hi. How about trying these codes below. 😄
mqtt!.enableSSL = true
mqtt!.allowUntrustCACertificate = true
Hi @leeway1208
yes, I did this ;)
I tried to connect with the following settings (this is working with MQTT.js for example)
The connection fails in this test (with errSSLXCertChainInvalid = -9807, / invalid certificate chain /)
func settings(mqtt: CocoaMQTT) {
mqtt.enableSSL = true
mqtt.allowUntrustCACertificate = true
mqtt.username = "admin"
mqtt.password = "password"
mqtt.keepAlive = 60
mqtt.autoReconnect = false
}
func testConnect() {
let mqtt = CocoaMQTT(clientID: "some-id",
host: host,
port: 8883)
settings(mqtt: mqtt)
let caller = Caller()
mqtt.delegate = caller
if !mqtt.connect() {
XCTAssertTrue(false)
}
wait_for {
caller.isConnected
}
}
However, the same test with WebSockets is working:
func testConnectWSS() {
let websocket = CocoaMQTTWebSocket(uri: "/")
let mqtt = CocoaMQTT(clientID: "some-id",
host: host,
port: 443,
socket: websocket)
settings(mqtt: mqtt)
let caller = Caller()
mqtt.delegate = caller
if !mqtt.connect() {
XCTAssertTrue(false)
}
wait_for {
caller.isConnected
}
}
SSL is handled completely different, the MQTT connection is using CocoaAsyncSocket and the WebSocket connection is using StarScream.
😄 hi @philipparndt Can you give me your host? I will test the connection. I just tried the host (https://test.mosquitto.org/) which is ok. Thanks~
😄 the host is on my local network with docker. The DNS name resolves to a Traefik container which uses LetsEncrypt to create a certificate.
I'll look how I can host this temporary in the internet and come back to you.
Hi @leeway1208 I have a server for you 😄
Try to connect to (no auth):
mqtts://cocoamqtt.rnd7.de:8883 wss://cocoamqtt.rnd7.de:443
The websocket connection will work wit CocoaMQTT, the MQTT connection fails with errSSLXCertChainInvalid = -9807, /* invalid certificate chain */
When I do the same with another MQTT Client (MQTT.js) both connections will work. Example:
import * as mqtt from "mqtt"
const client = mqtt.connect("mqtts://cocoamqtt.rnd7.de:8883")
client.on("connect", () => {
console.log("connected")
client.subscribe("#", (err) => { console.log(err, "subscribed") })
client.subscribe("$SYS/#", (err) => { console.log(err, "subscribed") })
})
client.on("message", (topic, message) => { console.log(topic, message.toString()) })
@philipparndt Thanks~ I will test it some days. If I have some new ideas, I will share with you.
@philipparndt I found that apple no longer supports TLS1.0 and TLS1.1. These versions have been deprecated on Apple platforms as of iOS 15, iPadOS 15, macOS 12, watchOS 8, and tvOS 15, and support will be removed in future releases. Do you use these versions? https://developer.apple.com/news/?id=bv8ur34d
Hi @leeway1208 thank your ideas. I did some changes in the configuration and checked the connection with Wireshark.
When the connection is done with the MQTT protocol, it uses the TLS1.2 Protocol with TLS version 1.2 from Server Hello message:
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 55
Handshake Protocol: Server Hello
TLSv1.2 Record Layer: Handshake Protocol: Certificate
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 875
Handshake Protocol: Certificate
TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 333
Handshake Protocol: Server Key Exchange
Handshake Type: Server Key Exchange (12)
Length: 329
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: secp256r1 (0x0017)
Pubkey Length: 65
Pubkey: …
Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
Signature Hash Algorithm Hash: SHA256 (4)
Signature Hash Algorithm Signature: RSA (1)
Signature Length: 256
Signature: …
TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 4
Handshake Protocol: Server Hello Done
after this, the client responds with Certificate unknown
Transport Layer Security
TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Certificate Unknown)
Content Type: Alert (21)
Version: TLS 1.2 (0x0303)
Length: 2
Alert Message
Level: Fatal (2)
Description: Certificate Unknown (46)
Another breadcrumb:
When I try to connect from the iPhone simulator using Apples Network Framework, the TLS handshake is successful. It seems that figuring out how to write a Network Framework Layer for CocoaMQTT is worth some time.
Example code
// Create an outbound connection
let connection = NWConnection(host: "cocoamqtt.rnd7.de", port: 8883, using: .tls)
connection.stateUpdateHandler = { (newState) in
print(newState)
}
connection.start(queue: DispatchQueue.global(qos: .userInitiated))
sleep(10)
Hi @leeway1208
okay I have a working demonstrator with Apple Network, that can connect to the server with the MQTT protocol 😄 There is still some work to do and as this protocol was introduced with iOS 12, I had to update the version number. See: https://github.com/philipparndt/CocoaMQTT/tree/apple-network
@philipparndt I try to solve the connection problems with GCDAsyncSocket. But I haven't found a solution yet. The function - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
is not called. When I connect to host (cocoamqtt.rnd7.de).
I will keep trying
@philipparndt I downloaded four mqtt apps in the App Store, and they can't connect to the host(mqtts://cocoamqtt.rnd7.de) either. So I discussed this with my colleague and he provided some reference documents for us to see if there are any configuration issues.
Enable SSL/TLS for EMQX MQTT broker (https://www.emqx.com/en/blog/emqx-server-ssl-tls-secure-connection-configuration-guide) Hands-on Demo: Set up TLS/SSL to Ensure MQTT Security (https://youtu.be/W7SedpZzQdo) https://www.youtube.com/watch?v=aFDDq9dLnEI (https://youtu.be/aFDDq9dLnEI)
Thanks
Hi @leeway1208, Thanks for your investigation!
What apps did you test?
I don't think that it is a configuration issue. Maybe the configuration is not supported by CocoaAsyncSocket
.
The configuration works like this:
This setup is pretty common when using Kubernetes (ingress controller). I think this will get used more and more and should be fixed/improved.
I have now exchanged CocoaAsyncSocket
with the Apple network framework on my branch, and it works perfectly. I still have to do more tests, but the following will work:
I will continue developing this branch and remove CocoaAsyncSocket
totally, as it has a lot of deprecated code (since iOS13). It should even be possible to handle WebSocket connections with this framework.
For your main branch I see the following options:
CocoaAsyncSocket
to support iOS < 12 but do not support newer TLS connectionsCocoaAsyncSocket
(I don't think it is worse it, as the Apple framework provides everything necessary)CocoaAsyncSocket
, one for Starscream
, one for Apple network framework
)@philipparndt Thanks for your help. I think you are right. It is more likely that CocoaAsyncSocket or some old libraries do not support this TLS configuration. I hope we can find some documentation to explain this issue and I am looking forward to receiving your pr. Then I will try to implement dynamic connections.
the testing app which I used below: EasyMQTT MQTTAnalyzer MQTTTool MQTT Spy
If you have some new ideas. We can discuss more. 😄😄😄😄😄😄
the testing app which I used below: EasyMQTT MQTTAnalyzer MQTTTool MQTT Spy
MQTTAnalyzer is my App and is using CocoaMQTT 😉 EasyMQTT is using CocoaMQTT as well 😉 MQTTTool is using Moscapsule (but was not updated for a long time)
I will come back to you when I have more. Currently, I implement connecting with client certificates.
@philipparndt haha. I find a way to connect the host. I write the sslSettings like this:
mqtt5!.allowUntrustCACertificate = true
mqtt5!.sslSettings = [kCFStreamSSLPeerName as String: "cocoamqtt.rnd7.de" as NSObject]
🎆🎆🎆
@philipparndt haha. I find a way to connect the host. I write the sslSettings like this:
mqtt5!.allowUntrustCACertificate = true mqtt5!.sslSettings = [kCFStreamSSLPeerName as String: "cocoamqtt.rnd7.de" as NSObject]
🎆🎆🎆
I can confirm this is working 👍 Nevertheless, I will continue the branch so that there is way less deprecated code 😉 I think we might need this for the next major iOS version anyway.
Hi @leeway1208,
I've implemented the changes on this branch: https://github.com/philipparndt/CocoaMQTT/pull/1 do you like to have a look at it?
While testing it I good some luck and seen some data races that are already reported by other users. I fixed them as well. Fixed:
@philipparndt Well done! Thank you for sharing this which I can practice in my code as well. I will study your code and start preparing for dynamic connections. Hope you can provide feedback when I'm done. Thanks.
@leeway1208 let me know when can help you or you have something we should discuss. Maybe it would be a good idea to specify some small API for the connection provider so that we can split-up the work a little bit.
@philipparndt Hi~~ Sorry, I can only start to work on this until May. But I can create a AppleNetwork branch for us first. haha
Hi,
when using the MQTT protocol with TLS, there are some issues (with versions 2.x and 1.x. LetsEncrypt certificates generated with Traefik are denied with
errSSLXCertChainInvalid = -9807, /* invalid certificate chain */
However, the same certificate works when connecting with WSS. I did some debugging, and think the problem is somewhere in the CocoaAsyncSocket which comes with a lot of deprecated API calls. Did you already do some experiments with Apples network framework to get rid of the deprecated API and maybe of this issue?
This issue was originally reported here: https://github.com/philipparndt/mqtt-analyzer/issues/69 see https://github.com/philipparndt/mqtt-analyzer/issues/69#issuecomment-1079704198 for a comparison between MQTTS and WSS (MQTTAnalyzer is using CocoaMQTT)