1. 程式人生 > >Windows Hello 淺嘗(2)

Windows Hello 淺嘗(2)

cee nis oot use bject find form fig ron

接著探索

上一篇講了一個微軟官方的Demo,這個Demo基本上已經把我們要做的事情做完了。

那麽基於這個Demo,我說下我的思路。

1.首先是要在界面程序中枚舉出藍牙設備

2.為使用藍牙設備的相關信息創建解鎖用的Windows Hello設備

3.當task收到解鎖事件的時候再次枚舉藍牙設備,然後判斷每個枚舉到的藍牙設備有沒有對應Windows Hello設備

4. 有就使用使用這個Windows Hello設備解鎖電腦

那這樣我先使用我的小米手環的信息創建對應的解鎖設備,當我的手環在附近的時候Task就能枚舉到它從而解鎖,當手環不在附近的時候Task自然就枚舉不到它,於是就不能使用Windows Hello解鎖。

開始實踐

關於枚舉藍牙設備,我又在微軟的官方例子中找到了Demo

Demo地址:https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/DeviceEnumerationAndPairing

這個Demo就展示了使用DeviceWatch去枚舉設備

DeviceWatcher m_deviceWatcher;
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected"
}; m_deviceWatcher = DeviceInformation.CreateWatcher("(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")", //m_deviceWatcher = DeviceInformation.CreateWatcher("(System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\")", requestedProperties, DeviceInformationKind.AssociationEndpoint); m_deviceWatcher.Added
+= DeviceWatcher_Added; m_deviceWatcher.Updated += DeviceWatcher_Updated; m_deviceWatcher.Removed += DeviceWatcher_Removed; m_deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted; m_deviceWatcher.Stopped += DeviceWatcher_Stopped; m_deviceWatcher.Start();

這個代碼應該是一目了然的, 其中的關鍵就在於CreateWatcher時候使用的參數 {bb7bb05e-5972-42b5-94fc-76eaa7084d49} 能枚舉到小米手環,但是枚舉不到手機 {e0cbf06c-cd8b-4647-bb8a-263b43f0f974} 則相反,能枚舉到手機但是枚舉不到小米手環。

這個類還能枚舉到其他很多的設備,就靠CreateWatcher的參數具體可以看官方的Demo。

下面是接口的定義,其中 DeviceInformation 和 DeviceInformationUpdate 就包含了設備的信息

private void DeviceWatcher_Added(DeviceWatcher Sender, DeviceInformation DeviceInfo)
{

}

private void DeviceWatcher_Updated(DeviceWatcher Sender, DeviceInformationUpdate DeviceInfoUpdate)
{

}

private void DeviceWatcher_Removed(DeviceWatcher Sender, DeviceInformationUpdate DeviceInfoUpdate)
{

}

private void DeviceWatcher_EnumerationCompleted(DeviceWatcher Sender, object e)
{

}

private void DeviceWatcher_Stopped(DeviceWatcher Sender, object e)
{

}

基本屬性都有對應的變量,而我在CreateWatcher的時候附加的屬性 string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" }; 會以鍵值對的形式保存在 DeviceInformation 和 DeviceInformationUpdate 的 Properties中

我首先在界面程序中使用這套接口枚舉到了我的手環,基本信息是這樣的

DeviceID: BluetoothLE#BluetoothLExx:xx:xx:xx:xx:xx-xx:xx:xx:xx:xx:xx

Name: MI Band 2

一開始我是想直接使用 BluetoothLE#BluetoothLExx:xx:xx:xx:xx:xx-xx:xx:xx:xx:xx:xx 這個ID去創建解鎖設備的,但是發現都失敗,實驗下來可能是只能使用GUID去創建解鎖設備。於是我就需要把 BluetoothLE#BluetoothLExx:xx:xx:xx:xx:xx-xx:xx:xx:xx:xx:xx 也存儲起來,存儲的思路

其實例子中也給出了就是存在解鎖設備的 DeviceConfigurationData 中,因為 我還要在 DeviceConfigurationData 中還要存儲解鎖用的兩個密碼,所以我幹脆就將在 DeviceConfigurationData 中存儲JSON格式的數據,專門寫了個序列化很反序列化的類。

public class JsonData
{
        
    public string Sign { get; set; }
       
    public string DeviceID { get; set; }
        
    public string DeviceKey { get; set; }
      
    public string AuthKey { get; set; }

    public bool CanUnLock { get; set; }
}

public class CMyHelloWinJson
{

    public static JsonData ParesToData(string js)
    {
        JsonData rv = null;
        try
        {
            JsonObject schoolObject = JsonObject.Parse(js);
            if (schoolObject != null)
            {
                rv = new JsonData();
                rv.Sign = schoolObject.GetNamedString("Sign");
                rv.DeviceID = schoolObject.GetNamedString("DeviceID");
                rv.DeviceKey = schoolObject.GetNamedString("DeviceKey");
                rv.AuthKey = schoolObject.GetNamedString("AuthKey");
                rv.CanUnLock = schoolObject.GetNamedBoolean("CanUnLock");
            }
        }catch(Exception e)
        {
            return rv;
        }
 
        return rv;
    }

    public static string ParesToJson(JsonData Dat)
    {
        JsonObject schoolObject = new JsonObject();
        schoolObject.SetNamedValue("Sign", JsonValue.CreateStringValue(Dat.Sign));
        schoolObject.SetNamedValue("DeviceID", JsonValue.CreateStringValue(Dat.DeviceID));
        schoolObject.SetNamedValue("DeviceKey", JsonValue.CreateStringValue(Dat.DeviceKey));
        schoolObject.SetNamedValue("AuthKey", JsonValue.CreateStringValue(Dat.AuthKey));
        schoolObject.SetNamedValue("CanUnLock", JsonValue.CreateBooleanValue(Dat.CanUnLock));
        return schoolObject.ToString();
    }
}

所以在界面中創建解鎖設備的代碼就變成了這樣

string modelnumber = "MyHelloWin";

// 隨機數轉 string 可能會丟失信息的
byte[] deviceKeyArray = new byte[32];
byte[] authKeyArray = new byte[32];

IBuffer deviceKey = CryptographicBuffer.CreateFromByteArray(deviceKeyArray);
IBuffer authKey = CryptographicBuffer.CreateFromByteArray(authKeyArray);

JsonData Dat = new JsonData();
Dat.Sign = "MyHellowin";

Dat.DeviceKey = System.Text.Encoding.UTF8.GetString(deviceKeyArray);

Dat.AuthKey = System.Text.Encoding.UTF8.GetString(authKeyArray);

Dat.DeviceID = DeviceID;

Dat.CanUnLock = false;

string js = CMyHelloWinJson.ParesToJson(Dat);
byte[] signArry = System.Text.Encoding.UTF8.GetBytes(js);
IBuffer deviceConfigData = CryptographicBuffer.CreateFromByteArray(signArry);
String deviceGUId = System.Guid.NewGuid().ToString();
int state = await WinHello.RegisterDeviceAsync(deviceGUId, bleDeviceDisplay.DeviceName, modelnumber, deviceConfigData, deviceKey, authKey);


// 註冊
SecondaryAuthenticationFactorDeviceCapabilities Capabilities = SecondaryAuthenticationFactorDeviceCapabilities.SecureStorage;
SecondaryAuthenticationFactorRegistrationResult RegistrationResult = await SecondaryAuthenticationFactorRegistration.RequestStartRegisteringDeviceAsync(DervicesID,
        Capabilities,
        FriendlyName,
        ModelNumber,
        deviceKey,
        authKey);
if (RegistrationResult.Status == SecondaryAuthenticationFactorRegistrationStatus.Started) {
    await RegistrationResult.Registration.FinishRegisteringDeviceAsync(DerviceContext);
}

return (int)RegistrationResult.Status;

界面寫好了,那麽解鎖的時候思路大致就是這樣的

先是枚舉出所有的藍牙設備,使用和上面相同的方法。

private void DeviceWatcher_Added(DeviceWatcher Sender, DeviceInformation DeviceInfo)
{

  IReadOnlyList<SecondaryAuthenticationFactorInfo> deviceList = await SecondaryAuthenticationFactorRegistration.FindAllRegisteredDeviceInfoAsync(SecondaryAuthenticationFactorDeviceFindScope.AllUsers);
  for (int i = 0; i < deviceList.Count; i++)
  {
      SecondaryAuthenticationFactorInfo deviceInfo = deviceList.ElementAt(i);

      byte[] combinedDataArray;
      CryptographicBuffer.CopyToByteArray(deviceInfo.DeviceConfigurationData, out combinedDataArray);
      string JS = System.Text.Encoding.UTF8.GetString(combinedDataArray);
      JsonData Dat = CMyHelloWinJson.ParesToData(JS);

      if (Dat == null)
      {
          continue;
      }

      if (String.Equals(Dat.DeviceID, DeviceInfo.DeviceID)) { // 使用這個設備進行解鎖  } 
  }
}

具體解鎖的方法,上面一篇文章中有相應的介紹。或者你直接看我寫的Demo: http://git.oschina.net/alwaysking/MyHelloWin

然而,當我天真的這樣就搞定的時候,來了個晴天霹靂,因為在Task中使用DeviceWatcher枚舉設備的時候他的回調函數根本不會被觸發。註意我的藍牙設備是配對的設備。

解決方法也是有的,我們下一篇再講

Windows Hello 淺嘗(2)