Azure IOT 中心 - 设备安全令牌
Azure IOT Hub - Device Security Token
所以我们使用 MQTT 连接 device/server。我使用 M2Mqtt 库使用模拟客户端,一切正常。我真正苦苦挣扎的是如何在代码中生成密码字段中使用的 signature。
我遵循了这个 https://azure.microsoft.com/en-us/documentation/articles/iot-hub-sas-tokens/ 但是我正在围绕 HMAC 方面进行斗争。他们所说的“** signingKey**”是什么?那是设备共享访问密钥吗?现在让模拟客户端在代码中创建自己的签名(而不是通过设备资源管理器)是必不可少的,然后我们甚至担心我们的现场产品是否可以计算这个(发现这对于现场设备来说真的太复杂了)。除了 node.js 以外,是否有我可以遵循的 C# 示例 - 此行是什么意思 "hmac.update(toSign);"
有没有更简单的方法来向服务器验证设备?也许只是使用它的共享访问密钥?
抱歉所有问题:/可能我只需要一个关于 what/when 的分步指南来执行 URI encode/Base64 encode/decode、HMAC 256 等,因为我相信文档是远远不够。
"{signature} HMAC-SHA256 签名字符串,格式为:{URL-encoded-resourceURI} + "\n" + expiry。重要提示:密钥从 base64 解码并用作密钥执行 HMAC-SHA256 计算。
页面 https://azure.microsoft.com/en-us/documentation/articles/iot-hub-sas-tokens/ 包含一个 Node.js 函数,可根据给定的输入生成 SAS 令牌。
根据您所说的,您正在使用令牌使设备能够连接到您的 IoT 中心,因此节点函数的输入应该是:
- 资源 URI:{IoT 中心名称}.azure-devices.net/devices/{设备 ID}.
- 签名密钥:{device id} 身份的任何对称密钥。您可以从 IoT 中心设备身份注册表获取此密钥 - 例如通过使用 DeviceExplorer 工具。
- 没有政策名称。
- 任何到期时间。
终于明白了:)
public static string getSaSToken()
{
TimeSpan fromEpochStart = DateTime.UtcNow - new DateTime(1970, 1, 1);
string expiry = Convert.ToString((int)fromEpochStart.TotalSeconds + 3600);
string baseAddress = "XYZABCBLAH.azure-devices.net/devices/12345".ToLower();
string stringToSign = WebUtility.UrlEncode(baseAddress).ToLower() + "\n" + expiry;
byte[] data = Convert.FromBase64String("y2moreblahblahblah=");
HMACSHA256 hmac = new HMACSHA256(data);
byte[] poo = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
string token = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}",
WebUtility.UrlEncode(baseAddress).ToLower(), WebUtility.UrlEncode(signature), expiry);
return token;
}
“12345”是我们设备的序列号。
y2z 的关键....将是我们的序列与其他奇特的东西的 base64 组合(只要它是 base64 格式以使集线器满意;))
有一天这对某人有帮助:
为 Azure IoT 中心构建授权 header
https://github.com/snobu/Azure-IoT-Hub/blob/master/make-token.sh
#!/usr/bin/env bash
#
# GitHub repo:
# https://github.com/snobu/Azure-IoT-Hub
#
# Construct authorization header for Azure IoT Hub
# https://azure.microsoft.com/en-us/documentation/articles/iot-hub-devguide/#security
#
# The security token has the following format:
# SharedAccessSignature sig={signature-string}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI}
#
# Author:
# Adrian Calinescu (a-adcali@microsoft.com), Twitter: @evilSnobu, github.com/snobu
#
# Many things borrowed from:
# http://whosebug.com/questions/20103258/accessing-azure-blob-storage-using-bash-curl
#
# Prereq:
# OpenSSL
# npm install underscore -g (for the tidy JSON colorized output) - OPTIONAL
# Python 2.6 (Might work with 2.5 too)
# curl (a build from this century should do)
urlencodesafe() {
# Use urllib to safely urlencode stuff
python -c "import urllib, sys; print urllib.quote_plus(sys.argv[1])"
}
iothub_name="heresthething"
apiversion="2015-08-15-preview"
req_url="${iothub_name}.azure-devices.net/devices?top=100&api-version=${apiversion}"
sas_key="eU2XXXXXXXXXXXXXXXXXXXXXXXXXXXXX="
sas_name="iothubowner"
authorization="SharedAccessSignature"
# 259200 seconds = 72h (Signature is good for the next 72h)
expiry=$(echo $(date +%s)+259200 | bc)
req_url_encoded=$(urlencodesafe $req_url)
string_to_sign="$req_url_encoded\n$expiry"
# Create the HMAC signature for the Authorization header
#
# In pseudocode:
# BASE64_ENCODE(HMAC_SHA256($string_to_sign))
#
# With OpenSSL it's a little more work (Whosebug thread at the top for details)
decoded_hex_key=$(printf %b "$sas_key" | base64 -d -w0 | xxd -p -c256)
signature=$(printf %b "$string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$decoded_hex_key" -binary | base64 -w0)
# URLencode computed HMAC signature
sig_urlencoded=$(urlencodesafe $signature)
# Print Authorization header
authorization_header="Authorization: $authorization sr=$req_url_encoded&sig=$sig_urlencoded&se=$expiry&skn=$sas_name"
echo -e "\n$authorization_header\n"
# We're ready to make the GET request against azure-devices.net REST API
curl -s -H "$authorization_header" "https://$req_url" | underscore print --color
echo -e "\n"
还有一个用于 Azure IoT 中心的示例 MQTT user/pass 组合(是的,密码很残酷并且包含一个空格):
https://github.com/Azure/azure-content/blob/master/articles/iot-hub/iot-hub-devguide.md#example
用户名(DeviceId区分大小写): iothubname.azure-devices.net/DeviceId
密码(使用 Device Explorer 生成 SAS): SharedAccessSignature sr=iothubname.azure-devices.net%2fdevices%2fDeviceId&sig=kPszxZZZZZZZZZZZZZZZZZAhLT%2bV7o%3d&se=1487709501
以下是如何在 Java 中生成 SAS 令牌:
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class AzureSasTokenCreator
{
public static void main(String[] args) throws InvalidKeyException, UnsupportedEncodingException,
MalformedURLException, NoSuchAlgorithmException
{
String token = generateSasTokenForIotDevice("myiothub.azure-devices.net/devices/mydevice",
"ZNILSsz4ke0r5DQ8rfB/PBWf6QqWGV7aaT/iICi9WTc=", 3600);
System.out.println(token);
}
private static String generateSasTokenForIotDevice(String uri, String devicePrimaryKey, int validtySeconds)
throws UnsupportedEncodingException, MalformedURLException, NoSuchAlgorithmException,
InvalidKeyException
{
Date now = new Date();
Date previousDate = new Date(1970);
long tokenExpirationTime = ((now.getTime() - previousDate.getTime()) / 1000) + validtySeconds;
String signature = getSignature(uri, tokenExpirationTime, devicePrimaryKey);
String token = String.format("SharedAccessSignature sr=%s&sig=%s&se=%s", uri, signature,
String.valueOf(tokenExpirationTime));
return token;
}
private static String getSignature(String resourceUri, long expiryTime, String devicePrimaryKey)
throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException
{
byte[] textToSign = new String(resourceUri + "\n" + expiryTime).getBytes();
byte[] decodedDeviceKey = Base64.getDecoder().decode(devicePrimaryKey);
byte[] signature = encryptHmacSha256(textToSign, decodedDeviceKey);
byte[] encryptedSignature = Base64.getEncoder().encode(signature);
String encryptedSignatureUtf8 = new String(encryptedSignature, StandardCharsets.UTF_8);
return URLEncoder.encode(encryptedSignatureUtf8, "utf-8");
}
private static byte[] encryptHmacSha256(byte[] textToSign, byte[] key)
throws NoSuchAlgorithmException, InvalidKeyException
{
SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA256");
Mac hMacSha256 = Mac.getInstance("HmacSHA256");
hMacSha256.init(secretKey);
return hMacSha256.doFinal(textToSign);
}
}
所以我们使用 MQTT 连接 device/server。我使用 M2Mqtt 库使用模拟客户端,一切正常。我真正苦苦挣扎的是如何在代码中生成密码字段中使用的 signature。
我遵循了这个 https://azure.microsoft.com/en-us/documentation/articles/iot-hub-sas-tokens/ 但是我正在围绕 HMAC 方面进行斗争。他们所说的“** signingKey**”是什么?那是设备共享访问密钥吗?现在让模拟客户端在代码中创建自己的签名(而不是通过设备资源管理器)是必不可少的,然后我们甚至担心我们的现场产品是否可以计算这个(发现这对于现场设备来说真的太复杂了)。除了 node.js 以外,是否有我可以遵循的 C# 示例 - 此行是什么意思 "hmac.update(toSign);"
有没有更简单的方法来向服务器验证设备?也许只是使用它的共享访问密钥?
抱歉所有问题:/可能我只需要一个关于 what/when 的分步指南来执行 URI encode/Base64 encode/decode、HMAC 256 等,因为我相信文档是远远不够。
"{signature} HMAC-SHA256 签名字符串,格式为:{URL-encoded-resourceURI} + "\n" + expiry。重要提示:密钥从 base64 解码并用作密钥执行 HMAC-SHA256 计算。
页面 https://azure.microsoft.com/en-us/documentation/articles/iot-hub-sas-tokens/ 包含一个 Node.js 函数,可根据给定的输入生成 SAS 令牌。 根据您所说的,您正在使用令牌使设备能够连接到您的 IoT 中心,因此节点函数的输入应该是:
- 资源 URI:{IoT 中心名称}.azure-devices.net/devices/{设备 ID}.
- 签名密钥:{device id} 身份的任何对称密钥。您可以从 IoT 中心设备身份注册表获取此密钥 - 例如通过使用 DeviceExplorer 工具。
- 没有政策名称。
- 任何到期时间。
终于明白了:)
public static string getSaSToken()
{
TimeSpan fromEpochStart = DateTime.UtcNow - new DateTime(1970, 1, 1);
string expiry = Convert.ToString((int)fromEpochStart.TotalSeconds + 3600);
string baseAddress = "XYZABCBLAH.azure-devices.net/devices/12345".ToLower();
string stringToSign = WebUtility.UrlEncode(baseAddress).ToLower() + "\n" + expiry;
byte[] data = Convert.FromBase64String("y2moreblahblahblah=");
HMACSHA256 hmac = new HMACSHA256(data);
byte[] poo = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
string token = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}",
WebUtility.UrlEncode(baseAddress).ToLower(), WebUtility.UrlEncode(signature), expiry);
return token;
}
“12345”是我们设备的序列号。 y2z 的关键....将是我们的序列与其他奇特的东西的 base64 组合(只要它是 base64 格式以使集线器满意;))
有一天这对某人有帮助:
为 Azure IoT 中心构建授权 header
https://github.com/snobu/Azure-IoT-Hub/blob/master/make-token.sh
#!/usr/bin/env bash
#
# GitHub repo:
# https://github.com/snobu/Azure-IoT-Hub
#
# Construct authorization header for Azure IoT Hub
# https://azure.microsoft.com/en-us/documentation/articles/iot-hub-devguide/#security
#
# The security token has the following format:
# SharedAccessSignature sig={signature-string}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI}
#
# Author:
# Adrian Calinescu (a-adcali@microsoft.com), Twitter: @evilSnobu, github.com/snobu
#
# Many things borrowed from:
# http://whosebug.com/questions/20103258/accessing-azure-blob-storage-using-bash-curl
#
# Prereq:
# OpenSSL
# npm install underscore -g (for the tidy JSON colorized output) - OPTIONAL
# Python 2.6 (Might work with 2.5 too)
# curl (a build from this century should do)
urlencodesafe() {
# Use urllib to safely urlencode stuff
python -c "import urllib, sys; print urllib.quote_plus(sys.argv[1])"
}
iothub_name="heresthething"
apiversion="2015-08-15-preview"
req_url="${iothub_name}.azure-devices.net/devices?top=100&api-version=${apiversion}"
sas_key="eU2XXXXXXXXXXXXXXXXXXXXXXXXXXXXX="
sas_name="iothubowner"
authorization="SharedAccessSignature"
# 259200 seconds = 72h (Signature is good for the next 72h)
expiry=$(echo $(date +%s)+259200 | bc)
req_url_encoded=$(urlencodesafe $req_url)
string_to_sign="$req_url_encoded\n$expiry"
# Create the HMAC signature for the Authorization header
#
# In pseudocode:
# BASE64_ENCODE(HMAC_SHA256($string_to_sign))
#
# With OpenSSL it's a little more work (Whosebug thread at the top for details)
decoded_hex_key=$(printf %b "$sas_key" | base64 -d -w0 | xxd -p -c256)
signature=$(printf %b "$string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$decoded_hex_key" -binary | base64 -w0)
# URLencode computed HMAC signature
sig_urlencoded=$(urlencodesafe $signature)
# Print Authorization header
authorization_header="Authorization: $authorization sr=$req_url_encoded&sig=$sig_urlencoded&se=$expiry&skn=$sas_name"
echo -e "\n$authorization_header\n"
# We're ready to make the GET request against azure-devices.net REST API
curl -s -H "$authorization_header" "https://$req_url" | underscore print --color
echo -e "\n"
还有一个用于 Azure IoT 中心的示例 MQTT user/pass 组合(是的,密码很残酷并且包含一个空格):
https://github.com/Azure/azure-content/blob/master/articles/iot-hub/iot-hub-devguide.md#example
用户名(DeviceId区分大小写): iothubname.azure-devices.net/DeviceId
密码(使用 Device Explorer 生成 SAS): SharedAccessSignature sr=iothubname.azure-devices.net%2fdevices%2fDeviceId&sig=kPszxZZZZZZZZZZZZZZZZZAhLT%2bV7o%3d&se=1487709501
以下是如何在 Java 中生成 SAS 令牌:
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class AzureSasTokenCreator
{
public static void main(String[] args) throws InvalidKeyException, UnsupportedEncodingException,
MalformedURLException, NoSuchAlgorithmException
{
String token = generateSasTokenForIotDevice("myiothub.azure-devices.net/devices/mydevice",
"ZNILSsz4ke0r5DQ8rfB/PBWf6QqWGV7aaT/iICi9WTc=", 3600);
System.out.println(token);
}
private static String generateSasTokenForIotDevice(String uri, String devicePrimaryKey, int validtySeconds)
throws UnsupportedEncodingException, MalformedURLException, NoSuchAlgorithmException,
InvalidKeyException
{
Date now = new Date();
Date previousDate = new Date(1970);
long tokenExpirationTime = ((now.getTime() - previousDate.getTime()) / 1000) + validtySeconds;
String signature = getSignature(uri, tokenExpirationTime, devicePrimaryKey);
String token = String.format("SharedAccessSignature sr=%s&sig=%s&se=%s", uri, signature,
String.valueOf(tokenExpirationTime));
return token;
}
private static String getSignature(String resourceUri, long expiryTime, String devicePrimaryKey)
throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException
{
byte[] textToSign = new String(resourceUri + "\n" + expiryTime).getBytes();
byte[] decodedDeviceKey = Base64.getDecoder().decode(devicePrimaryKey);
byte[] signature = encryptHmacSha256(textToSign, decodedDeviceKey);
byte[] encryptedSignature = Base64.getEncoder().encode(signature);
String encryptedSignatureUtf8 = new String(encryptedSignature, StandardCharsets.UTF_8);
return URLEncoder.encode(encryptedSignatureUtf8, "utf-8");
}
private static byte[] encryptHmacSha256(byte[] textToSign, byte[] key)
throws NoSuchAlgorithmException, InvalidKeyException
{
SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA256");
Mac hMacSha256 = Mac.getInstance("HmacSHA256");
hMacSha256.init(secretKey);
return hMacSha256.doFinal(textToSign);
}
}