有奖捉虫:行业应用 & 管理与支持文档专题 HOT

概述

设备向平台发起 HTTP/HTTPS 请求时,请求报文中需包含签名信息(X-TC-Signature)以验证请求者身份。

签名步骤

设备请求报文示例:
curl -X POST https://ap-guangzhou.gateway.tencentdevices.com/device/register \\
-H "Content-Type: application/json; charset=utf-8" \\
-H "X-TC-Algorithm: hmacsha256" \\
-H "X-TC-Timestamp: 155****065" \\
-H "X-TC-Nonce: 5456" \\
-H "X-TC-Signature: 2230eefd229f582d8b1b891af****b91597240707d778ab3738f756258d7652c" \\
-d '{"ProductId":"ASJ****GX","DeviceName":"xyz"}'

1. 拼接签名字符串

StringToSign =
HTTPRequestMethod + \\n +
CanonicalHost + \\n +
CanonicalURI + \\n +
CanonicalQueryString + \\n +
Algorithm + \\n +
RequestTimestamp + \\n +
Nonce + \\n +
HashedCanonicalRequest
参数名称
描述
HTTPRequestMethod
HTTP 请求方式, 支持 POST。
CanonicalHost
HTTP 请求的 Host 地址。
CanonicalURI
HTTP 请求 URI 。例如 https://ap-guangzhou.gateway.tencentdevices.com/device/register 的 URI 为 /device/register
CanonicalQueryString
发起 HTTP 请求 URL 中的查询字符串,对于 POST 请求,固定为空字符串""
Algorithm
签名方法。目前支持 hmacsha256 和 hmacsha1。
RequestTimestamp
请求时间戳,单位:秒。
Nonce
随机数。
HashedCanonicalRequest
请求正文 Hash 值。HTTP 请求正文做 SHA256 哈希,然后十六进制编码,最后编码串转换成小写字母。
根据以上的规则,示例中的规范签名串如下:
POST
ap-guangzhou.gateway.tencentdevices.com
/device/register

hmacsha256
155****065
5456
35e9c5b0e3ae67532d3c9f17ead6c902226****b1ff7f6e89887f1398934f064


2. 计算签名

使用密钥签名,包括产品级密钥和设备级密钥,伪代码如下:
Signature = Base64_Encode(HMAC_SHA256(SignSecret, StringToSign))
参数名称
描述
SignSecret
签名密钥,如动态注册使用 ProductSecret,设备发布消息或上报日志则使用 psk。
StringToSign
待签名的字符串。
使用证书签名,伪代码如下:
Signature = Base64_Encode(RSA_SHA256(PrivateKey, StringToSign))
参数名称
描述
PrivateKey
证书私钥,如设备发布消息或上报日志则使用设备的 X509 私钥证书。
StringToSign
待签名的字符串。

3. 组装请求报文

根据上述得到的签名串,最终完整的请求如下:
POST https://ap-guangzhou.gateway.tencentdevices.com/devregister
Content-Type: application/json
Host: ap-guangzhou.gateway.tencentdevices.com
X-TC-Algorithm: hmacsha256
X-TC-Timestamp: 155****065
X-TC-Nonce: 5456
X-TC-Signature: 2230eefd229f582d8b1b891af71****1597240707d778ab3738f756258d7652c


{"ProductId":"ASJ****GX","DeviceName":"xyz"}

示例代码

C 语言示例代码

工程下载 后,在 Linux 环境进行编译,编译指令:
./cmake_build.sh
编译成功后,按如下指令执行签名示例:
./out/bin/qcloud-dynreg-sign product_id product_secretkey device_name

Python3 示例代码

import hashlib
import random
import time
import hmac
import base64

if __name__ == '__main__':
sign_format = '%s\\n%s\\n%s\\n%s\\n%s\\n%d\\n%d\\n%s'
url_format = '%s://ap-guangzhou.gateway.tencentdevices.com/device/register'
request_format = "{\\"ProductId\\":\\"%s\\",\\"DeviceName\\":\\"%s\\"}"
device_name = 'dev***'
product_id = 'JCZ****KXS'
product_secret = 'X42fPqw********94cY5sQ1Y'

request_text = request_format % (product_id, device_name)
request_hash = hashlib.sha256(request_text.encode("utf-8")).hexdigest()

nonce = random.randrange(2147483647)
timestamp = int(time.time())
sign_content = sign_format % (
"POST", "ap-guangzhou.gateway.tencentdevices.com",
"/device/register", "", "hmacsha256", timestamp,
nonce, request_hash)
print("\\nsign_content: \\n" + sign_content)

sign_base64 = base64.b64encode(hmac.new(product_secret.encode("utf-8"),
sign_content.encode("utf-8"), hashlib.sha256).digest())

print("sign_base64: " + str(sign_base64))


Java 示例代码

package com.tencent.iot.hub.device.java.core.sign;

import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;

import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.fail;

public class SignForHttpTest {

private static final String HMAC_ALGO = "hmacsha256";

@Test
public void testSign() {
try {
JSONObject yourPayload = new JSONObject();
String signature = getSignature("YourProductId", "YourDeviceName",
"YourDevicePsk", "YourTopicName", yourPayload, 0);
System.out.println(signature);
assertTrue(true);
} catch (Exception e) {
e.printStackTrace();
fail();
}
}

public static String getSignature(String productID, String deviceName, String devicePsk,
String topicName, JSONObject payload, Integer qos) {
int randNum = (int) (Math.random() * ((1 << 31) - 1));
int timestamp = (int) (System.currentTimeMillis() / 1000);
final JSONObject obj = new JSONObject();
try {
obj.put("ProductId", productID);
obj.put("DeviceName", deviceName);
obj.put("TopicName", topicName);
obj.put("Payload", payload.toString());
obj.put("Qos", qos);
} catch (JSONException e) {
e.printStackTrace();
}
String originRequest = obj.toString();
String hashedRequest = "";
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(originRequest.getBytes(Charset.forName("UTF-8")));
hashedRequest = HMACSHA256.bytesToHexString(encodedhash);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}

@SuppressWarnings("DefaultLocale")
String signSourceStr = String.format("%s\\n%s\\n%s\\n%s\\n%s\\n%d\\n%d\\n%s",
"POST",
"ap-guangzhou.gateway.tencentdevices.com",
"/device/publish",
"",
HMAC_ALGO,
timestamp,
randNum,
hashedRequest
);

return HMACSHA256.getSignature(signSourceStr.getBytes(), devicePsk.getBytes());
}

public static class HMACSHA256 {

/**
* 生成签名数据
*
* @param data 待加密的数据
* @param key 加密使用的key
* @return 生成16进制编码的字符串
*/
public static String getSignature(byte[] data, byte[] key) {
Mac mac;
SecretKeySpec signKey = new SecretKeySpec(key, HMAC_ALGO);
try {
mac = Mac.getInstance(HMAC_ALGO);
mac.init(signKey);
byte[] rawHmac = mac.doFinal(data);
return Base64.getEncoder().encodeToString(rawHmac);
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}

/**
* byte[]数组转换为16进制的字符串
*
* @param bytes 要转换的字节数组
* @return 转换后的结果
*/
private static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
}

}