Laden...

Xamarin - Shelly 1 Plus - Digest Authentifizierung

Erstellt von ChristophSharp vor einem Jahr Letzter Beitrag vor einem Jahr 1.890 Views
C
ChristophSharp Themenstarter:in
6 Beiträge seit 2022
vor einem Jahr
Xamarin - Shelly 1 Plus - Digest Authentifizierung

Hallo liebe Leute,

Möchte in meiner Xamarin Forms App einen Shelly 1 Pro per Url ansprechen.
Seit der Pro Version wird eine Digest Authentifizierung benötigt ansonsten ist der Shelly ungeschützt.

Wenn ich die Authentifizierung ausschalte funktioniert der nachfolgende Code:


 private void LmuButton(object sender, EventArgs e)
             {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://192.168.10.68/relay/0?turn=on");
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            }

Die Doko vom Shelly beinhaltet JavaScript Code die ich bisher leider nicht richtig in c# übersetzen kann.

https://shelly-api-docs.shelly.cloud/gen2/Overview/CommonDeviceTraits/#authentication

Hier Ausschnitte aus der Doku

Schritte im Prozess:

Der Client fordert eine geschützte Ressource an, ohne Anmeldeinformationen bereitzustellen.
Eine Serverantwort mit Fehler 401 (nicht autorisiert) wird empfangen.
Der Client fordert dieselbe geschützte Ressource an, gibt aber diesmal Anmeldeinformationen an.
Die Anforderung ist erfolgreich und der Zugriff auf die Ressource wird gewährt.
Bei der Kommunikation über HTTP muss dieser Vorgang für jede Anfrage wiederholt werden, die Sie an das Gerät senden. Für die Kommunikation über Websocket müssen die Schritte 1 und 2 jedoch nicht mehr als einmal durchlaufen werden: Sie müssen das authObjekt nur einmal erstellen (mehr dazu weiter unten ) und es dann für alle aufeinanderfolgenden Anforderungen an dieses Gerät verwenden.

Authentifizierung über
Bei der Kommunikation über einen HTTP-Kanal finden Sie diese Informationen im WWW-Authenticate- Header der Antwort.

Beispiel:

Anfrage
curl -X POST -i -d '{"id":1, "method":"Shelly.GetStatus"}' http://${SHELLY}/rpc
Antwort


HTTP/1.1 401 Unauthorized
Server: Mongoose/6.18
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Content-Length: 0
Connection: close
WWW-Authenticate: Digest qop="auth", realm="shellypro4pm-f008d1d8b8b8", nonce="60dc59c6", algorithm=SHA-256

Authentifizierung über
Bei der Kommunikation über einen Websocket-Kanal enthält die Antwort einen Fehlerrahmen, in dem die oben genannten Attribute zu finden sind.

Beispiel:

Anfrage
curl -X POST -d '{"id":1, "method":"Shelly.DetectLocation"}' http://${SHELLY}/rpc
Antwort
{
"id": 1,
"src": "shellypro4pm-f008d1d8b8b8",
"dst": "user_1",
"error": {
"code": 401,
"message": "{"auth_type": "digest", "nonce": 1625038762, "nc": 1, "realm": "shellypro4pm-f008d1d8b8b8", "algorithm": "SHA-256"}"
}
}
Erfolgreiche Anfrage mit
Damit die Anfrage richtig funktioniert, authsollte dem Anfragerahmen ein zusätzliches JSON-Objekt hinzugefügt werden. Es sollte enthalten:


realm: string, device_id des Shelly-Geräts. Erforderlich
username: string, muss auf admin gesetzt werden . Erforderlich
nonce: Zahl, Zufalls- oder Pseudo-Zufallszahl zur Verhinderung von Replay-Angriffen, entnommen aus der Fehlermeldung. Erforderlich
cnonce: Nummer, Client-Nonce, vom Client generierte Zufallszahl. Erforderlich
response: string, Kodierung des Strings <ha1> + ":" + <nonce> + ":" + <nc> + ":" + <cnonce> + ":" + "auth" + ":" + <ha2>in SHA256. Erforderlich
ha1: Zeichenfolge, <user>:<realm>:<password>codiert in SHA256
ha2: string, "dummy_method:dummy_uri" codiert in SHA256
algorithm: Zeichenkette, SHA-256 . Erforderlich

Beispiel:

Anfrage1


curl -X POST -d '{"id":1, "method":"Shelly.DetectLocation", "auth":
 {"realm": "shellypro4pm-f008d1d8b8b8", "username": "admin", "nonce": 1625038762,
 "cnonce": 313273957, "response": "eab75cbbd7acdb7082164cb52148cfbe351f28bf80856f93a23387c6157dbb69",
 "algorithm": "SHA-256"}}' \
  http://${SHELLY}/rpc

Oder:

Anfrage2
curl --digest -u admin:mypass http://${SHELLY}/rpc/Shelly.DetectLocation
Oder:

Anfrage3
curl --anyauth -u admin:mypass -X POST -d '{"id":1, "method":"Shelly.DetectLocation"}' http://${SHELLY}/rpc
Antwort


{
   "id": 1,
   "src": "shellypro4pm-f008d1d8b8b8",
   "dst": "user_1",
   "result": {
      "tz": "Europe/Sofia",
      "lat": 42.67236,
      "lon": 23.38738
   }
}

Ich hoffe mir kann einer weiterhelfen?😉

Viele Grüße
Christoph

16.806 Beiträge seit 2008
vor einem Jahr

Was mir auf den ersten Blick auffällt, ohne Shelly zu kennen:

  • Dein Request erfolgt ohne Verschlüsselung (kein HTTPS)
  • Du hast keinerlei asynchronen Code. Synchroner Code, der blockiert und damit die App unbenutzbar macht, führt auf mobilen Plattformen jedoch dazu, dass die App zurecht abgeschossen wird
  • Die Shelly Dokumentation zeigt kein Basic Auth, sondern Digest Auth

In .NET sollte man mit dem HttpClient arbeiten, nicht mit HttpWebRequest.
Der .NET HTTP Client nimmt dann auch die Arbeit bei einfachen Auth-Verfahren wie eben Basic und Digest ab.
Schritte:

  • HttpClientHandler erzeugen, der ein Credential Objekt hält (hier wahrscheinlich NetworkCredential)
  • Http Client erstellen und dabei den Handler übergeben
  • Url aufrufen

Die .NET ist sehr sehr ausführlich was das Thema HttpClient und/oder Simple Credential Handling angeht, zB NetworkCredential Class (System.Net)
Man muss sie nur lesen 😉

C
ChristophSharp Themenstarter:in
6 Beiträge seit 2022
vor einem Jahr

Hallo,
Abt vielen Dank für die Antwort.

Zu Punkt 1 . Verschlüsselung ist mit dem Shelly nicht möglich. Nur Http!

Habe noch einmal etwas zusammengebastelt.
Funktioniert auch wieder ohne Authentifizierung.
Mit der Authentifizierung kommt Fehlercode
System.Net.WebException: 'The remote server returned an error: (401) Unauthorized.'

Hier der Code


 private void LmuButton(object sender, EventArgs e)
        {

            var user = "admin";
            var password = "password";
            var url = "http://192.168.10.68/relay/0?turn=on";




            NetworkCredential myCred = new NetworkCredential(
            user, password);

            CredentialCache myCache = new CredentialCache();

            myCache.Add(new Uri(url), "Digest", myCred);
            

            WebRequest wr = WebRequest.Create(url);
            wr.Credentials = myCache;


            var client = new HttpClient();
            HttpWebResponse response = (HttpWebResponse)wr.GetResponse();


        }

Ja der Code ist noch asynchron.
Hat noch jemand einen Tipp?
Vielen Dank schon mal!

Viele Grüße
Christoph

16.806 Beiträge seit 2008
vor einem Jahr

Und auf den HttpClientHandler hattest keine Lust? 😉

P
441 Beiträge seit 2014
vor einem Jahr

Dein HttpClient macht nichts, du verwendest in deinem Code weiterhin den HttpWebRequest.

C
ChristophSharp Themenstarter:in
6 Beiträge seit 2022
vor einem Jahr
Hilfe benötigt

Habe mich bei der Thematik eingelesen und mehreres ausprobiert.
Komme leider einfach nicht weiter. Hat eventuell einer einen Beispielcode der genau meine Problematik abdeckt.
Oder kann mir einer einen passenden Code für kleines Geld schreiben?