tinkerborg / thinq2-python

Reverse-engineered client for LG ThinQ v2 IoT devices
GNU General Public License v3.0
69 stars 19 forks source link

Trying to port to VB.Net #7

Open stefxx opened 4 years ago

stefxx commented 4 years ago

Not an issue, but a question. Hopefully you allow me to post it here as I have been searching the net for days without result. I am trying to use the AWS IoT MQTT from the new v2 API, in my .Net project. Something is wrong with my certificate, but the code runs fine until I try to connect to the AWS MQTT. I can't find the issue, what do I miss?

Most important parts of my code below. I use the M2Mqtt for the actual MQTT connection. After I have been successfully authenticated to the API, this is what I do:

' Do a GET and (empty) POST to /service/users/client. Not sure why this is neccessary.
result = LG_API(API_ThinQ2_Url & "/service/users/client", "GET")
result = LG_API(API_ThinQ2_Url & "/service/users/client", "POST", "")

' Create a new public/private key pair, and create a CSR
Dim RSA2048 As RSA = RSA.Create(2048)
Dim distinguishedName As X500DistinguishedName = New X500DistinguishedName("CN=AWS IoT Certificate, O=Amazon")
Dim req As CertificateRequest = New CertificateRequest(distinguishedName, RSA2048, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)

' Save the private and public key
Dim CertPrivKey = Certificate.ExportPrivateKey(RSA2048)
File.WriteAllText(Path.GetTempPath() & "Certificate.private.key", CertPrivKey)
Dim CertPubKey = Certificate.ExportPublicKey(RSA2048)
File.WriteAllText(Path.GetTempPath() & "Certificate.public.key", CertPubKey)

' Create the actual request in PEM format. Make sure the change the Accept header to */*
CertCSR = PemEncodeSigningRequest(req)
result = LG_API(API_ThinQ2_Url & "/service/users/client/certificate", "POST", "{""csr"": """ & CertCSR & """}", "*/*")
Dim Cert As CertificateClass = JsonConvert.DeserializeObject(Of CertificateClass)(result, New JsonDeserializeSettings)

' Save the certificate as PEM file
CertPem = Cert.result.certificatePem
File.WriteAllText(Path.GetTempPath() & "Certificate.pem", CertPem)

' Add the private key to the certificate and save as Pfx
Dim Cert2 = New X509Certificate2()
Cert2.Import(Path.GetTempPath() & "Certificate.pem")
Dim CertWithKey As X509Certificate2 = Cert2.CopyWithPrivateKey(RSA2048)
File.WriteAllBytes(Path.GetTempPath() & "Certificate.pfx", certWithKey.Export(X509ContentType.Pfx, "P@SSW0rd"))

' Get the CA certificate and save as PEM
CertCA = LG_API("https://www.websecurity.digicert.com/content/dam/websitesecurity/digitalassets/desktop/pdfs/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem", "GET", "", "*/*")
File.WriteAllText(Path.GetTempPath() & "root.pem", CertCA)

' Get the required MQTT endpoints
result = LG_API("https://common.lgthinq.com/route", "GET")
Dim Route As RouteClass = JsonConvert.DeserializeObject(Of RouteClass)(result, New JsonDeserializeSettings)
Dim Uri As New Uri(Route.result.mqttServer)
Dim iotEndpoint As String = Uri.Host
Dim brokerPort As Integer = Uri.Port

' Get the certificates
Dim clientCert = New X509Certificate2(Path.GetTempPath & "Certificate.pfx", "P@SSW0rd")
Dim caCert = New X509Certificate(Path.GetTempPath & "root.pem")

' Setup the MqttClient and attach the certificates
Dim client = New MqttClient(iotEndpoint, brokerPort, True, caCert, clientCert, MqttSslProtocols.TLSv1_2)
AddHandler client.MqttMsgPublishReceived, AddressOf Client_MqttMsgPublishReceived
AddHandler client.MqttMsgSubscribed, AddressOf Client_MqttMsgSubscribed
Dim clientId As String = Guid.NewGuid().ToString()

' Connect to the AWS IoT MQTT
client.Connect(clientId)

Note that LG_API is simply returning the data from the API. The client.Connect results in the following error: "AuthenticationException: The remote certificate is invalid according to the validation procedure."

Anything obvious I am missing here? Thanks!!

tinkerborg commented 4 years ago

If it's obvious, I'm not seeing it. This all looks correct to me. It looks like your client can't verify the server cert. If you save out the certs, you could try connecting with openssl CLI as a test, e.g.:

openssl s_client -CAfile ca.pem -cert client.pem -key client.key -connect your-endpoint.amazonaws.com:8883

Toward the top of the output you will see the cert chain displayed, followed connection errors, if any.

stefxx commented 4 years ago

Thanks! I didn't know that functionality existed in openssl. Here is the output. The only thing that seems weird is "No client certificate CA names sent". Do you see anything? How is this different from your output?

openssl.exe s_client -CAfile root.pem -cert Certificate.pem -key Certificate.private.key -connect a3phael99lf879.iot.eu-west-1.amazonaws.com:8883
CONNECTED(000001CC)
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
verify return:1
depth=1 C = US, O = Symantec Corporation, OU = Symantec Trust Network, CN = Symantec Class 3 ECC 256 bit SSL CA - G2
verify return:1
depth=0 C = US, ST = Washington, L = Seattle, O = "Amazon.com, Inc.", CN = *.iot.eu-west-1.amazonaws.com
verify return:1
---
Certificate chain
 0 s:C = US, ST = Washington, L = Seattle, O = "Amazon.com, Inc.", CN = *.iot.eu-west-1.amazonaws.com
   i:C = US, O = Symantec Corporation, OU = Symantec Trust Network, CN = Symantec Class 3 ECC 256 bit SSL CA - G2
 1 s:C = US, O = Symantec Corporation, OU = Symantec Trust Network, CN = Symantec Class 3 ECC 256 bit SSL CA - G2
   i:C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDhjCCAy2gAwIBAgIQTfk5sPa6DChDHubftqXP5zAKBggqhkjOPQQDAjCBgDEL
MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD
VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBD
bGFzcyAzIEVDQyAyNTYgYml0IFNTTCBDQSAtIEcyMB4XDTE5MDkyNTAwMDAwMFoX
DTIwMDkyNTIzNTk1OVowdzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0
b24xEDAOBgNVBAcMB1NlYXR0bGUxGTAXBgNVBAoMEEFtYXpvbi5jb20sIEluYy4x
JjAkBgNVBAMMHSouaW90LmV1LXdlc3QtMS5hbWF6b25hd3MuY29tMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAE+sMQQ9ziswGnuaXUWsZCcOuCCal2EcIOaez+Qulm
Y07rlJrUimyGCLmb/Aj5Qq24Z8Z56P+W01jQaiCKm8DpQKOCAY8wggGLMEUGA1Ud
EQQ+MDyCG2lvdC5ldS13ZXN0LTEuYW1hem9uYXdzLmNvbYIdKi5pb3QuZXUtd2Vz
dC0xLmFtYXpvbmF3cy5jb20wCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwHQYD
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMGEGA1UdIARaMFgwVgYGZ4EMAQIC
MEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUF
BwICMBkMF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBhMB8GA1UdIwQYMBaAFCXwiuFL
etkBlQrtxlPxjHgf2fP4MCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9yYy5zeW1j
Yi5jb20vcmMuY3JsMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDov
L3JjLnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3JjLnN5bWNiLmNvbS9y
Yy5jcnQwCgYIKoZIzj0EAwIDRwAwRAIgWdb4xELd5tJTMvSFhMFvAuFdxfXtTSdh
I/hjEjVeRKMCIAM1hKy+SV2PFbm73bgVS2UfLJnVCX11ohSo/LrcftK1
-----END CERTIFICATE-----
subject=C = US, ST = Washington, L = Seattle, O = "Amazon.com, Inc.", CN = *.iot.eu-west-1.amazonaws.com

issuer=C = US, O = Symantec Corporation, OU = Symantec Trust Network, CN = Symantec Class 3 ECC 256 bit SSL CA - G2

---
No client certificate CA names sent
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: ECDSA+SHA512:RSA+SHA512:ECDSA+SHA384:RSA+SHA384:ECDSA+SHA256:RSA+SHA256:DSA+SHA256:ECDSA+SHA224:RSA+SHA224:DSA+SHA224:ECDSA+SHA1:RSA+SHA1:DSA+SHA1
Shared Requested Signature Algorithms: ECDSA+SHA512:RSA+SHA512:ECDSA+SHA384:RSA+SHA384:ECDSA+SHA256:RSA+SHA256:DSA+SHA256:ECDSA+SHA224:RSA+SHA224:DSA+SHA224:ECDSA+SHA1:RSA+SHA1:DSA+SHA1
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 2385 bytes and written 1633 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES256-GCM-SHA384
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-ECDSA-AES256-GCM-SHA384
    Session-ID: 8AB86E770555486501A273E33075B77B4501E1677321EDD32C0DBB5E11C0CE16
    Session-ID-ctx:
    Master-Key: 00406975048F1069B20A2A443EAB1CDD4DEA954BE3078022D7BDD3721E61F1DD2C9A4353A2097FCA0DDF4801D5ED25EB
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1589354261
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: yes
---
tinkerborg commented 4 years ago

Looks good to me, that confirms your certs are good. I guess you need to look at why M2MQTT is not trusting the server cert.

stefxx commented 4 years ago

Yes, agree. Unfortunately, .Net expects the cert and private key to be send together in a pfx, so it seems something isn't right there. Thanks for your help!

stefxx commented 4 years ago

A little progress, but stuck again. There was nothing wrong with the certs indeed. Also the pfx is fine. However, .Net checks the certificate against the Windows certification store, so I either need to import the CA root cert, or define a cert callback and check within code. So far so good.

However, now the connection simply hangs at connecting, until a timeout occurs. Tried both MQTTnet and M2Mqtt libraries.

"Exception of type 'MQTTnet.Exceptions.MqttCommunicationTimedOutException' was thrown."

Anyone any suggestion? Could it be something with ALPN? I can't find any way to set this with any of the MQTT libraries.