Azure IoT Hub 入門 - 權限管理


最初開始接觸Azure IoT Hub的時候,被各種connection string和endpoint弄的眼花繚亂。本入門系列旨在將Azure IoT Hub 權限管理機制以及各個接口(endpoint)的用途解釋清楚。

首先拋出一個典型的IoT解決方案的架構,以讓讀者對IoT有個大概的認識。該架構通過平台層的核心服務和應用層組件來實現典型IoT解決方案需要解決的三個主要問題:

  • 設備的連接;
  • 數據的處理、分析與管理;
  • 數據的有效呈現以及業務邏輯的處理。

回到本文的主題:IoT Hub 權限管理以及Endpoint。

    1. Azure IoT Hub 權限管理
      總結起來,Azure 提供了以下兩種權限管理機制:
      • Hub 層面的共享訪問策略(shared access policies)
        在portal上新創建的IoT Hub默認包含了以下策略,你可以對已有的策略進行修改,或者添加新的策略。
        • iothubowner: 擁有所有的權限
        • service: ServiceConnect 權限 (給予服務端通信監控接口訪問權限,例如讀取device-to-cloud的消息,發送cloud-to-device消息等)
        • device: DeviceConnect 權限(給予設備端的通信接口訪問權限,例如發送device-to-cloud消息)
        • registryRead: RegistryRead 權限(讀設備注冊列表)
        • registryReadWrite: RegistryRead和RegistryWrite權限(讀寫設備注冊列表)  
      • device 層面的安全令牌(security credentials)
        IoT Hub維護了一個所有設備的注冊列表。列表里的每一個設備都有自己的symmetric key,用戶可以根據這個symmetric key 來獲得DeviceConnect的權限。 
      針對特定場景下所需要的權限舉例如下:
      * 設備管理組件:registryReadWrite 策略
      * 事物處理組件:service 策略
      * 單設備連接組件:device策略
  1. 如何生成安全令牌
    為避免直接在網絡上傳輸密鑰,IoT Hub通過安全令牌來對設備以及雲端服務進行授權。一般情況下,Azure IoT Hub SDKs會自動根據密鑰生成安全令牌。但在某些情況下(例如直接使用MQTT,AMQP或者HTTP接口)需要客戶自己去生成安全令牌。
    安全令牌的格式如下:
    SharedAccessSignature sig={signature-string}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI}

    針對每個字段的注解,請參考:https://azure.microsoft.com/en-us/documentation/articles/iot-hub-devguide-security/
    下面給出完整的C#實現:

    public class SharedAccessSignatureBuilder
        {
            private string key;
    
            public string Key
            {
                get
                {
                    return this.key;
                }
                set
                {
                    // StringValidationHelper.EnsureBase64String(value, "Key");
                    this.key = value;
                }
            }
    
            public string KeyName
            {
                get;
                set;
            }
    
            public string Target
            {
                get;
                set;
            }
    
            public TimeSpan TimeToLive
            {
                get;
                set;
            }
    
            public string TargetService
            {
                get;
                set;
            }
    
            public SharedAccessSignatureBuilder()
            {
                this.TimeToLive = TimeSpan.FromMinutes(20);
                TargetService = "iothub";
            }
    
            private static string BuildExpiresOn(TimeSpan timeToLive)
            {
                DateTime dateTime = DateTime.UtcNow.Add(timeToLive);
                TimeSpan timeSpan = dateTime.Subtract(SharedAccessSignatureConstants.EpochTime);
                return Convert.ToString(Convert.ToInt64(timeSpan.TotalSeconds, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
            }
    
            private static string BuildSignature(string keyName, string key, string target, TimeSpan timeToLive, string targetService = "iothub")
            {
                string str = SharedAccessSignatureBuilder.BuildExpiresOn(timeToLive);
                string str1 = WebUtility.UrlEncode(target);
                List<string> strs = new List<string>()
                {
                    str1,
                    str
                };
                string str2 = SharedAccessSignatureBuilder.Sign(string.Join("\n", strs), key, targetService);
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}={2}&{3}={4}&{5}={6}", new object[] { "SharedAccessSignature", "sr", str1, "sig", WebUtility.UrlEncode(str2), "se", WebUtility.UrlEncode(str) });
                if (!string.IsNullOrEmpty(keyName))
                {
                    stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "&{0}={1}", new object[] { "skn", WebUtility.UrlEncode(keyName) });
                }
                return stringBuilder.ToString();
            }
    
            private static string Sign(string requestString, string key, string targetService)
            {
                string base64String;
    
                if (!string.IsNullOrEmpty(targetService) && targetService.ToLower() == "servicebus")
                {
                    using (HMACSHA256 hMACSHA256 = new HMACSHA256(Encoding.UTF8.GetBytes(key))) // key is not decoded
                    {
                        base64String = Convert.ToBase64String(hMACSHA256.ComputeHash(Encoding.UTF8.GetBytes(requestString)));
                    }
                }
                else
                {
                    using (HMACSHA256 hMACSHA256 = new HMACSHA256(Convert.FromBase64String(key))) // key is decoded
                    {
                        base64String = Convert.ToBase64String(hMACSHA256.ComputeHash(Encoding.UTF8.GetBytes(requestString)));
                    }
                }
    
                return base64String;
            }
    
            public string ToSignature()
            {
                return SharedAccessSignatureBuilder.BuildSignature(this.KeyName, this.Key, this.Target, this.TimeToLive, this.TargetService);
            }
        }
    SharedAccessSignatureBuilder
    public class SharedAccessSignatureConstants
        {
            public const int MaxKeyNameLength = 256;
    
            public const int MaxKeyLength = 256;
    
            public const string SharedAccessSignature = "SharedAccessSignature";
    
            public const string AudienceFieldName = "sr";
    
            public const string SignatureFieldName = "sig";
    
            public const string KeyNameFieldName = "skn";
    
            public const string ExpiryFieldName = "se";
    
            public const string SignedResourceFullFieldName = "SharedAccessSignature sr";
    
            public const string KeyValueSeparator = "=";
    
            public const string PairSeparator = "&";
    
            public readonly static DateTime EpochTime;
    
            public readonly static TimeSpan MaxClockSkew;
    
            static SharedAccessSignatureConstants()
            {
                SharedAccessSignatureConstants.EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
                SharedAccessSignatureConstants.MaxClockSkew = TimeSpan.FromMinutes(5);
            }
        }
    SharedAccessSignatureConstants

    你也可以直接引用我編譯的DLL 文件,並在項目中引用它。 

    using SharedAccessSignatureGenerator;

    調用方式:

    1)根據IoT Hub 共享訪問策略生成安全令牌
    假設IoT Hub 共享訪問策略連接串如下:

    HostName=<iot-hub-name>.azure-devices.cn;SharedAccessKeyName=<policy name>;SharedAccessKey=<key>

    調用方法:

    var sasBuilder = new SharedAccessSignatureBuilder()
                    {
                        KeyName = <policy name>,
                        Key = <key>,
                        Target = string.Format("{0}/devices", <iot-hub-name>),
                        TimeToLive = TimeSpan.FromDays(Convert.ToDouble(ttlValue))
                    };
    string sas = sasBuilder.ToSignature();

    輸出示例:

    SharedAccessSignature sr=devpod.azure-devices.cn%2Fdevices&sig=eAtQg7Du%2FUBrBk9zELLpOwELyGSVuOH0qHv1iJ63xnc%3D&se=1478756374&skn=iothubowner

    2)根據設備的symmetric key生成安全令牌

    假設IoT Hub 設備的鏈接串如下:

    HostName=<iot-hub-name>.azure-devices.cn;DeviceId=<deviceId>;SharedAccessKey=<key>

    調用方法:

    var sasBuilder = new SharedAccessSignatureBuilder()
                    {
                        Key = <key>,
                        Target = string.Format("{0}/devices/{1}", <iot-hub-name>, WebUtility.UrlEncode(<deviceId>)),
                        TimeToLive = TimeSpan.FromDays(Convert.ToDouble(ttlValue))
                    };
    string sas = sasBuilder.ToSignature();

    輸出示例:

    SharedAccessSignature sr=devpod.azure-devices.cn%2Fdevices%2Fdevice001&sig=x6rceLUBASP99GU03LAX5w0YQ8gF05J6%2BYX5gJwKISQ%3D&se=1478756286

      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM