# 数据上传文档
# mqtt上传数据
设备通过mqtt协议接入鉴权通过后,可以通过publish消息向HanClouds上传设备数据。单条数据最大8KB。
# 上传数据流
topic:data/{deviceKey}/{stream}
- data为固定前缀
- deviceKey必须为本设备的
deviceKey
,否则平台会拒绝数据上传并断开mqtt连接,网关代理的子设备亦使用自身的deviceKey上报数据。 - stream为物模型定义的数据流标识符,为英文字符串,不可包含符号、特殊字符和中文
payload:设备上传的数据,定义了物模型的数据流,payload需匹配对应物模型,否则上报数据平台将断开与设备连接。
数据类型包括: -int int数据类型 -string 字符串类型 -enum 枚举类型 -float 浮点数据类型 -double double数据类型 -json json格式数据 -bin bin数据类型,base64编码的数据 -date 日期数据 -array 数组类型 -gps 地理位置数据 -boolean boolean数据类型
- payload在签名模式下为用
dynamicSecret
加密的密文,在非签名模式下为数据明文
# 上传事件
topic:event/{deviceKey}/{identifier}
- event为固定前缀
- topic中
deviceKey
必须为本设备的deviceKey
,网关代理的子设备上传事件时,这里的deviceKey
为子设备的deviceKey
- identifier为物模型定义的事件标识符。
payload : 匹配物模型事件模板的json数据;
- payload在签名模式下为用
dynamicSecret
加密的密文,在非签名模式下为数据明文
# 加密流程说明
在设备以签名模式连接到HanClouds后,默认后续数据会采用动态秘钥进行加密传输。其加解密流程如下:
- device 以签名模式向HanClouds发送Connect消息
- HanClouds收到请求并鉴权通过后,生成一个动态秘钥
dynamicSecret
, 用deviceSecret
加密后再通过topic rsp/welcome topic 发送给device - device收到加密后的
dynamicSecret
,根据签名模式不同,使用AES或者SM4对deviceSecret
解密,得到dynamicSecret
明文。 - 后续publish消息中的payload用
dynamicSecret
作为秘钥进行加密后传输
# 加密算法说明
1.SHA1签名模式
- 加密算法为AES/CBC/Pkcs5padding
- 加密时使用的iv固定为16字节,payload部分的头部16字节为iv,每次加密的iv都不同。例如对某个数据用秘钥A和iv加密后得到加密结果字节数组长度为32字节,则最终传递到网络中的内容为48字节,密文前面附加了16字节的iv.
大部分语言都提供了AES的标准库,Java代码示例如下:
/**
* 使用AES加密
*
*/
public static byte[] encodeWithAesCbc(String secret, byte[] content) {
if (secret.length() < 16) {
return null;
}
if (secret.length() > 16) {
secret = secret.substring(0, 16);
}
try {
SecretKeySpec key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] ivBytes = getRandomString(16).getBytes(StandardCharsets.UTF_8);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] encrypData = cipher.doFinal(content);
if (encrypData != null) {
//将iv附加到加密内容中
byte[] result = new byte[16 + encrypData.length];
System.arraycopy(ivBytes, 0, result, 0, 16);
System.arraycopy(encrypData, 0, result, 16, encrypData.length);
return result;
}
return null;
} catch (Exception e) {
logger.error("encodeWithAesCbc(...) failed, method = AES/CBC/PKCS5Padding, cause={}", e.getMessage());
}
return null;
}
/**
* 使用AES解密
*
*/
public static byte[] decodeWithAesCbc(String secret, byte[] content) {
if (secret.length() < AES_KEY_SIZE) {
return null;
}
if (secret.length() > AES_KEY_SIZE) {
secret = secret.substring(0, AES_KEY_SIZE);
}
try {
SecretKeySpec key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] ivBytes = new byte[16];
//从内容中取出iv
System.arraycopy(content, 0, ivBytes, 0, 16);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] encBytes = new byte[content.length - 16];
System.arraycopy(content, 16, encBytes, 0, encBytes.length);
return cipher.doFinal(encBytes);
} catch (Exception e) {
logger.error("decodeWithAesCbc(...) failed, {}", e.getMessage());
}
return null;
}
2.国密SM签名模式
- 加密算法为SM4/CBC/Pkcs5padding
- 加密时使用的iv固定为16字节,payload部分的头部16字节为iv,每次加密的iv都不同。例如对某个数据用秘钥A和iv加密后得到加密结果字节数组长度为32字节,则最终传递到网络中的内容为48字节,密文前面附加了16字节的iv.
SM4则需要使用第三方标准实现,首先添加Maven依赖:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
Java代码示例如下:
public class SM4Demo{
static {
//添加三方标准算法实现
Security.addProvider(new BouncyCastleProvider());
}
/**
* 使用SM4加密
*
*/
public static byte[] encodeWithSm4Cbc(String secret, byte[] content) {
if (secret.length() < 16) {
return null;
}
if (secret.length() > 16) {
secret = secret.substring(0, 16);
}
try {
SecretKeySpec key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "SM4");
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding");
byte[] ivBytes = getRandomString(16).getBytes(StandardCharsets.UTF_8);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] encrypData = cipher.doFinal(content);
if (encrypData != null) {
//将iv附加到加密内容中
byte[] result = new byte[16 + encrypData.length];
System.arraycopy(ivBytes, 0, result, 0, 16);
System.arraycopy(encrypData, 0, result, 16, encrypData.length);
return result;
}
return null;
} catch (Exception e) {
logger.error("encodeWithSm4Cbc(...) failed, method = SM4/CBC/PKCS5Padding, cause={}", e.getMessage());
}
return null;
}
/**
* 使用SM4解密
*
*/
public static byte[] decodeWithAesCbc(String secret, byte[] content) {
if (secret.length() < AES_KEY_SIZE) {
return null;
}
if (secret.length() > AES_KEY_SIZE) {
secret = secret.substring(0, AES_KEY_SIZE);
}
try {
SecretKeySpec key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "SM4");
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding");
byte[] ivBytes = new byte[16];
//从内容中取出iv
System.arraycopy(content, 0, ivBytes, 0, 16);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] encBytes = new byte[content.length - 16];
System.arraycopy(content, 16, encBytes, 0, encBytes.length);
return cipher.doFinal(encBytes);
} catch (Exception e) {
logger.error("decodeWithSm4Cbc(...) failed, {}", e.getMessage());
}
return null;
}
}
# http上传数据
请参考restful api文档: 设备上传数据