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
Was mir auf den ersten Blick auffällt, ohne Shelly zu kennen:
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:
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 😉
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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
Und auf den HttpClientHandler hattest keine Lust? 😉
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Dein HttpClient macht nichts, du verwendest in deinem Code weiterhin den HttpWebRequest.
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?