I want to setup a photons wifi with C# (respectivly with xamarin for android / iOS / WindowsMobile).
Is there any real documentation for the softAP setup - meaning detailed information of the workflow and the posts and request? Or do i need to extract that information out of the existing code for e.g. the softap JavaScript solution.
Ok - it needs to be RSA encrypted. unfortunatly i have no experience at all with encryption. Maybe someone can help me out?
i can succesfully connect to a photon in listening mode, get the device id, the public key and the scanned AccesPoints - so i am at a point where i want to configure the access point.
.net has implemented a RSACryptoServiceProvider i’m trying to use - and i already have the public key.
does this received public key need to be converted?
i need to pass an XMLstring to the encrypt function…
it obviously needs to be modified somehow - truncated even? - but how did you get it in the right format?
i tried the obvious methods to get an byteArray of a string (like System.Buffer.BlockCopy, or System.Text.Encoding.UTF8/ASCII.GetBytes) but importing them throws an “unexpected format” exception.
The public key is in hex format similar to what is defined by the X.509 standard except written out as hex bytes. So in the example you posted above, there are many bytes of flags etc. and then the bytes starting from 0x00 0x98 0x85....0x4f 0x7f 0xc4 are the 1024-bit modulus and them more overhead bytes and then 0x10 0x00 0x01 is the exponent, in this case the common value of 2^16+1 or 65537. The long sequence of zeros after the exponent is where the key would be signed if this were a signed key.
It looks like the library posted by @polystyrene above can read this from a byte array using
So I think you just need to get the hex bytes into an array and call that library function.
It works!. Your help is very much appreciated. i’ll post my full code later in case anyone needs it.
edit: here is my simple test code in vb.net if anyone is interested:
Imports System.Net
Imports System.Runtime.InteropServices
Imports System.Security.Cryptography
Imports System.Text
Imports System.Web.Script.Serialization
Imports PCLCrypto
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim jss As New JavaScriptSerializer
Dim devID = jss.Deserialize(Of device_idResponse)(getData(RequestType.DeviceID))
Dim pubKey = jss.Deserialize(Of publicKeyResponse)(getData(RequestType.PublicKey))
Debug.WriteLine(String.Format("Device ID: {0}", devID.id))
Debug.WriteLine(String.Format("Public Key: {0} | {1}", pubKey.b, pubKey.r))
Dim d = System.Text.Encoding.UTF8.GetBytes(pubKey.b)
Dim EncryptedKey = EncryptString(">password_here<", StringToByteArray(pubKey.b))
Debug.WriteLine(EncryptedKey.ToString)
Dim EncryptedKeyString = ByteArrayToString(EncryptedKey)
Debug.WriteLine(String.Format("Encrypted PWD: {0}", EncryptedKeyString))
Debug.WriteLine("Scanned APs:")
Dim scans = jss.Deserialize(Of scanAPResult)(getData(RequestType.ScannedWifi))
For Each i In scans.scans
Debug.WriteLine(i.ssid)
If i.ssid.ToLower.Contains("office") Then ' just testing out!
Using client As New WebClient
Dim apData As New configAP
With apData
.ch = i.ch
.idx = 0
.pwd = EncryptedKeyString
.sec = i.sec
.ssid = i.ssid
End With
Dim apDataStr = jss.Serialize(apData)
client.UploadString("http://192.168.0.1/configure-ap", apDataStr)
System.Threading.Thread.Sleep(1000)
Dim connectData As New connectAP
connectData.idx = 0
client.UploadString("http://192.168.0.1/connect-ap", jss.Serialize(connectData))
End Using
End If
Next
End Sub
Public Function EncryptString(inputString As String, keyBuffer As Byte()) As Byte()
Dim algorithm = WinRTCrypto.AsymmetricKeyAlgorithmProvider.OpenAlgorithm(PCLCrypto.AsymmetricAlgorithm.RsaPkcs1)
Dim publicKey = algorithm.ImportPublicKey(keyBuffer)
Dim plainBuffer = WinRTCrypto.CryptographicBuffer.ConvertStringToBinary(inputString, Encoding.UTF8)
Return WinRTCrypto.CryptographicEngine.Encrypt(publicKey, plainBuffer)
End Function
Private Function getData(req As RequestType) As String
Using client As New WebClient
Select Case req
Case RequestType.DeviceID
Return client.DownloadString("http://192.168.0.1/device-id")
Case RequestType.PublicKey
Return client.DownloadString("http://192.168.0.1/public-key")
Case RequestType.ScannedWifi
Return client.DownloadString("http://192.168.0.1/scan-ap")
Case Else
Return ""
End Select
End Using
End Function
Public Shared Function StringToByteArray(hex As String) As Byte()
Return Enumerable.Range(0, hex.Length).Where(Function(x) x Mod 2 = 0).[Select](Function(x) Convert.ToByte(hex.Substring(x, 2), 16)).ToArray()
End Function
Public Shared Function ByteArrayToString(ba As Byte()) As String
Dim hex As New StringBuilder(ba.Length * 2)
For Each b As Byte In ba
hex.AppendFormat("{0:x2}", b)
Next
Return hex.ToString()
End Function
End Class
Public Enum RequestType
DeviceID
PublicKey
ScannedWifi
End Enum
<Serializable> Public Class device_idResponse
Public Property id As String
Public Property c As String
End Class
<Serializable> Public Class publicKeyResponse
Public Property b As String
Public Property r As Integer
End Class
<Serializable> Public Class scanAPResult
Public Property scans As ObjectModel.Collection(Of Scan)
End Class
<Serializable> Public Class Scan
Public Property ssid As String
Public Property rssi As Integer
Public Property sec As Integer
Public Property ch As Integer
Public Property mdr As Integer
End Class
Public Class configAP
Public Property idx As Integer
Public Property ssid As String
Public Property sec As Integer
Public Property ch As Integer
Public Property pwd As String
End Class
Public Class connectAP
Public Property idx As Integer
End Class
Sorry, I should have included that additional hex string/byte array conversion code as well. It looks like you’re using exactly what I have as well now.
Did your Photon take the AP settings without any issue? I am using System.Net.HttpClient, and I ran into the following problem:
I had to bypass HttpClient for that call, and implement the HTTP POST myself over TCP. Kind of a pain.
I have been PM’d to share the Code (for Xamarin C#) so i thought i could as well post it in here for others to find and use. It worked fine - but i decided to go down the web/Meteor route so i dont use it anymore. See the using Statements for used packages / nugets.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Fragment = Android.Support.V4.App.Fragment;
using System.Net;
using PCLCrypto;
using Newtonsoft.Json;
using Android.Net.Wifi;
namespace NavDrawer.Fragments
{
public class wifiSetupFragment : Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
device_idResponse devID;
publicKeyResponse pubKey;
scanAPResult scan;
void WiFiSetup()
{
devID = JsonConvert.DeserializeObject<device_idResponse>(getData(RequestType.DeviceID));
pubKey = JsonConvert.DeserializeObject<publicKeyResponse>(getData(RequestType.PublicKey));
scan = JsonConvert.DeserializeObject<scanAPResult>(getData(RequestType.ScannedWifi));
Spinner APChooser = View.FindViewById<Spinner>(Resource.Id.scannedAPSpinner);
ArrayAdapter cityAdapter = new ArrayAdapter(ctx, Android.Resource.Layout.SimpleSpinnerItem);
cityAdapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
foreach (Scan AP in scan.scans)
{
cityAdapter.Add(AP.ssid);
};
APChooser.Adapter = cityAdapter;
}
void WiFiFinish()
{
Scan SelectedAp=new Scan();
String SelectedApStr;
foreach(Scan AP in scan.scans)
{
SelectedApStr = View.FindViewById<Spinner>(Resource.Id.scannedAPSpinner).SelectedItem.ToString();
if (AP.ssid == SelectedApStr)
{
SelectedAp = AP;
};
};
Byte[] EncryptedKey = EncryptString(View.FindViewById<TextView>(Resource.Id.wifiPWBox).Text, StringToByteArray(pubKey.b));
var EncryptedKeyString = ByteArrayToString(EncryptedKey);
using (WebClient client = new WebClient())
{
configAP apData = new configAP();
apData.ch = SelectedAp.ch;
apData.idx = 0;
apData.pwd = EncryptedKeyString;
apData.sec = SelectedAp.sec;
apData.ssid = SelectedAp.ssid;
var apDataStr = JsonConvert.SerializeObject(apData);
client.UploadString("http://192.168.0.1/configure-ap", apDataStr);
System.Threading.Thread.Sleep(1000);
connectAP connectData = new connectAP();
connectData.idx = 0;
client.UploadString("http://192.168.0.1/connect-ap", JsonConvert.SerializeObject(connectData));
}
}
public byte[] EncryptString(string inputString, byte[] keyBuffer)
{
var algorithm = WinRTCrypto.AsymmetricKeyAlgorithmProvider.OpenAlgorithm(PCLCrypto.AsymmetricAlgorithm.RsaPkcs1);
var publicKey = algorithm.ImportPublicKey(keyBuffer);
var plainBuffer = WinRTCrypto.CryptographicBuffer.ConvertStringToBinary(inputString, Encoding.UTF8);
return WinRTCrypto.CryptographicEngine.Encrypt(publicKey, plainBuffer);
}
private string getData(RequestType req)
{
using (WebClient client = new WebClient())
{
switch (req)
{
case RequestType.DeviceID:
return client.DownloadString("http://192.168.0.1/device-id");
case RequestType.PublicKey:
return client.DownloadString("http://192.168.0.1/public-key");
case RequestType.ScannedWifi:
return client.DownloadString("http://192.168.0.1/scan-ap");
default:
return "";
}
}
}
Context ctx;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Android.OS.Bundle savedInstanceState)
{
ctx = container.Context;
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
var view = inflater.Inflate(Resource.Layout.fragment_WiFiSetup, null);
Button btWifiSetup = view.FindViewById<Button>(Resource.Id.btWifiSetupStart);
Button btWifiFinish = view.FindViewById<Button>(Resource.Id.btFinishWifiSetup);
btWifiSetup.Click += delegate
{
WiFiSetup();
};
btWifiFinish.Click += delegate
{
WiFiFinish();
};
return view;
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).ToArray();
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
}
public enum RequestType
{
DeviceID,
PublicKey,
ScannedWifi
}
[Serializable()]
public class device_idResponse
{
public string id { get; set; }
public string c { get; set; }
}
[Serializable()]
public class publicKeyResponse
{
public string b { get; set; }
public int r { get; set; }
}
[Serializable()]
public class scanAPResult
{
public System.Collections.ObjectModel.Collection<Scan> scans { get; set; }
}
[Serializable()]
public class Scan
{
public string ssid { get; set; }
public int rssi { get; set; }
public int sec { get; set; }
public int ch { get; set; }
public int mdr { get; set; }
}
public class configAP
{
public int idx { get; set; }
public string ssid { get; set; }
public int sec { get; set; }
public int ch { get; set; }
public string pwd { get; set; }
}
public class connectAP
{
public int idx { get; set; }
}
any ideas what might be going wrong so I can make it work?
also i noticed that you imported the key as a whole without extracting the modulus and the exponent, is that ok? because i was looking at the JS script from another thread “softap-usage” and it was extracting the modulus and the exponent from the key string 1st.
I think I found the problem, the encrypted password hex string is 258 characters not 256. Although my password id only 8 characters “12345678”. What do you guys think I am missing?
appreciate your help