一型一密
一型一密两种使用方式:
● 一型一密预注册:
设备联网前,需要在物联网平台预注册设备DeviceName,建议采用设备的MAC地址、IMEI、SN码等作为DeviceName。物联网平台为设备颁发DeviceSecret。
云端鉴权成功后,设备采用设备证书(ProductKey、DeviceName和DeviceSecret)与云端建立通信连接。
支持通过MQTT通道、HTTP通道进行一型一密预注册认证。
● 一型一密免预注册:
不需要在物联网平台预注册设备DeviceName,便于使用物联网卡卡号等作为DeviceName。
云端鉴权成功后,设备采用ProductKey、ProductSecret、ClientID和DeviceToken与云端建立通信连接。
支持通过MQTT通道进行一型一密预注册认证。
一型一密免预注册
此处记录免预注册几个要点:
- 建立TLS通道 ,TCP通道不行。
- 免预注册仅支持MQTT方式,不支持HTTP方式。
- 注册方式 与MQTT普通连接方法相同 ,只是连接请求中包含的元素不同。
- instanceId,实例ID,阿里云手册说在物联网平台控制台实例管理页面查看,实际上公共实例没有ID,这个参数就不需要。如果公共实例,却填了该参数,connect时会收到回复04错误,看起来像是用户名或密码不对,实际上是这个参数错误。
- 注册连接成功后,服务器会自动回复参数。
- 然后可以主动断开连接,也可以等15s自动断开连接。
代码
此处贴三个函数,一个是注册参数初始化与连接,一个是正常参数初始化连接,还有连接函数。
其中,连接函数中固定报头不一定是2字节,实际上得看可变报头+有效负荷的长度,在一部分自编的连接函数案例中,都是固定的2字节。
//创建TLS通道,然后发起MQTT注册连接
void ali_register_connect(void)
{
char content[128] = {0};
sprintf(content,"deviceName%sproductKey%srandom123",device_name,product_key);
char mqttPassword[64] = {0};
utils_hmac_sha1((const char*)content,strlen((const char*)content),(char *)mqttPassword,product_secret,product_secret_len);
char mqttClientId[128] = {0};
sprintf((char *)mqttClientId,"%s|securemode=-2,authType=regnwl,random=123,signmethod=hmacsha1|",device_name);
char mqttUserName[64]={0};
sprintf((char *)mqttUserName,"%s&%s",device_name,product_key);
mqtt_connect((char *)mqttClientId,(char *)mqttUserName,(char *)mqttPassword);
//此处可延时,然后获取回复后进行解析
......
}
// 建立TCP连接,然后使用注册时返回的clientid和devicetoken,发起MQTT连接
int ali_normal_connect(void)
{
char clientID[64]={0};
char deviceToken[64]={0};
STMFLASH_Read(ALI_CLIENTID_ADDR,(uint16_t *)clientID,32);
STMFLASH_Read(ALI_DEVICETOKEN_ADDR,(uint16_t *)deviceToken,32);
strcat(clientID,"|securemode=-2,authType=connwl|");
char mqttUserName[64]={0};
sprintf((char *)mqttUserName,"%s&%s",device_name,product_key);
mqtt_connect((char *)clientID,(char *)mqttUserName,(char *)deviceToken);
rt_thread_mdelay(100);
uint8_t buffer[10]={0};
ring_buf_read(&g_uart_rx_buf,sizeof(buffer),buffer);
if(buffer[0]==0x20 && buffer[3]==0x00)
{
return SUCCESS;
}
return ERROR;
}
//连接函数,可以移植,也可以字节编写。以下是自编的。
void mqtt_connect(char *ClientId,char *Username,char *Password)
{
uint8_t buffer[256]={0};
uint16_t num=0;
uint8_t Fixed_len = 2; //连接报文中,固定报头长度=2
uint8_t Variable_len = 10; //连接报文中,可变报头长度=10
uint16_t ClientId_len = strlen(ClientId);
uint16_t Username_len = strlen(Username);
uint16_t Password_len = strlen(Password);
uint16_t Payload_len = 2 + ClientId_len + 2 + Username_len + 2 + Password_len; //总报文长度
/************************** 报头 *************************/
buffer[num++]=0x10; //固定报头第1个字节:0x01
uint16_t length =Variable_len + Payload_len;
do
{
char d = length % 128;
length /= 128;
/* if there are more digits to encode, set the top bit of this digit */
if (length > 0)
d |= 0x80;
buffer[num++] = d; //固定报头 :可变报头+有效负荷的长度
} while (length > 0);
buffer[num++]=0x00; //可变报头 :固定0x00
buffer[num++]=0x04; //可变报头 :固定0x04
buffer[num++]='M'; //可变报头 :固定0x4D
buffer[num++]='Q';; //可变报头 :固定0x51
buffer[num++]='T'; //可变报头 :固定0x54
buffer[num++]='T'; //可变报头 :固定0x54
buffer[num++]=0x04; //可变报头 :固定0x04
buffer[num++]=0xC2; //可变报头 :使能用户名和密码校验,不使用遗嘱,不保留会话
buffer[num++]=0x00; //可变报头 :保活时间高字节 0x00
buffer[num++]=0x64; //可变报头 :保活时间高字节 0x64 100s
/************************** 有效载荷 Payload *************************/
/* CLIENT_ID */
buffer[num++] = ClientId_len/256; //客户端ID长度高字节
buffer[num++] = ClientId_len%256; //客户端ID长度低字节
memcpy(&buffer[num],ClientId,ClientId_len); //复制客户端ID字串
num = num + ClientId_len;
/* 用户名 */
buffer[num++] = Username_len/256; //用户名长度高字节
buffer[num++] = Username_len%256; //用户名长度低字节
memcpy(&buffer[num],Username,Username_len); //复制用户名字串
num = num + Username_len;
/* 密码 */
buffer[num++] = Password_len/256; //密码长度高字节
buffer[num++] = Password_len%256; //密码长度低字节
memcpy(&buffer[num],Password,Password_len); //复制密码字串
num = num + Password_len;
HAL_UART_Transmit(&huart1, buffer, num ,100);
}