WifiSetup with C# - need help with Encryption

Hi,

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.

Any hint would be appreciated.

i just discovered

Particle.Net

Device Wifi Setup seems not to be a part of it though.

Nevermind i just used Fiddler while using the softAP Setup and it’s easy enough.

What kind of encryption is used to transfer the WiFi Key?

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…

can anyone help me?

I did it using PCLCrypto. Looks like this:

    public static byte[] EncryptString( string inputString, byte[] keyBuffer )
    {
        var algorithm = WinRTCrypto.AsymmetricKeyAlgorithmProvider.OpenAlgorithm( AsymmetricAlgorithm.RsaPkcs1 );
        var publicKey = algorithm.ImportPublicKey( keyBuffer );
        var plainBuffer = WinRTCrypto.CryptographicBuffer.ConvertStringToBinary( inputString, Encoding.UTF8 );

        return WinRTCrypto.CryptographicEngine.Encrypt( publicKey, plainBuffer );
    }
1 Like

Thank you @polystyrene!

How did you get your public key to the expected format?.

So i get the public key as a string like:

30819F300D06092A864886F70D010101050003818D00308189028181009885DC94E34A23A2942BB9EB6721C4233E9EDCC9A967F587CEA527E1D447F48319CA6C4178DFF739C0AB079E02467DD4D3AD3214416F0983C3967EA71378D7D93A885F1575D71D009990BFFC0882FC721F4DC98A0D80B4CCF12E51066D69055E9A3C95E247BEB9DC16176A083DE7FA93C23449A3870D599DA9D507964F7FC4B90203010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

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.

Maybe @bko can help out? you did show some knowledge of the keys here:

(-;

Hi @smnnekho

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.

1 Like

Thank you @polystyrene, @bko!

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.

This bug has been fixed in 0.4.7 I believe:

once i got the code figured out i had no further problem (i am running 0.4.7). The photon restarts and connects to WiFi when i send

client.UploadString("http://192.168.0.1/connect-ap", jss.Serialize(connectData))
1 Like

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; }
}

}

thanks @smnnekho for posting the code. Thanks for everyone else for your great input

I tried the code on a WinForms application, but it doesn’t work. The data is posted but the Photon is sill blinking blue (listening mode)

Does anyone know how can I accomplish this using .Net System.Security.Cryptography class?

I am getting {“r”:-1040} reply from the server. Where can I find the errors returns by the SoftAP requests?

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.

Thanks

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

Quick update… I removed the 1st 2 characters in the string and it now works!!!
Thanks a lot for the code @smnnekho

@polystyrene where you able to use httpclient to post the requests?