mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-18 00:45:55 +08:00
177 lines
5.1 KiB
C++
177 lines
5.1 KiB
C++
/*********************************************************************
|
||
* function: 设备认证
|
||
* board: esp8266 core for arduino v3.0.2
|
||
* library: PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
|
||
* source: https://gitee.com/kerwincui/wumei-smart
|
||
* copyright: wumei-smart and kerwincui all rights reserved.
|
||
********************************************************************/
|
||
|
||
#include "Auth.h"
|
||
|
||
/*
|
||
* 连接MQTT,加密认证
|
||
*/
|
||
void connectMqtt()
|
||
{
|
||
printMsg("连接Mqtt服务器...");
|
||
mqttClient.setClient(wifiClient);
|
||
mqttClient.setServer(mqttHost, mqttPort);
|
||
mqttClient.setBufferSize(1024);
|
||
mqttClient.setKeepAlive(5);
|
||
// 设置Mqtt回调函数
|
||
mqttClient.setCallback(mqttCallback);
|
||
|
||
// 生成mqtt加密密码
|
||
String aesPassword = generationAESPwd();
|
||
// 连接 设备mqtt客户端Id格式为:认证类型(E=加密、S=简单) & 设备编号 & 产品ID & 用户ID
|
||
String clientId = "E&" + (String)deviceNum + "&" + (String)productId + "&" + (String)userId;
|
||
printMsg("客户端ID:" + clientId);
|
||
bool connectResult = mqttClient.connect(clientId.c_str(), mqttUserName, aesPassword.c_str());
|
||
if (connectResult)
|
||
{
|
||
printMsg("连接Mqtt成功");
|
||
// 订阅系统主题
|
||
subscribeTopic();
|
||
// 发布设备信息,设备上电都需要发布一次
|
||
publishInfo();
|
||
}
|
||
else
|
||
{
|
||
printMsg("连接失败, rc=");
|
||
Serial.print(mqttClient.state());
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 生成加密密码
|
||
*/
|
||
String generationAESPwd()
|
||
{
|
||
// 获取NTP时间
|
||
String jsonTime = getTime();
|
||
StaticJsonDocument<128> doc;
|
||
DeserializationError error = deserializeJson(doc, jsonTime);
|
||
if (error)
|
||
{
|
||
printMsg("Json解析失败:");
|
||
Serial.print(error.f_str());
|
||
return "";
|
||
}
|
||
// 获取NTP时间=(设备发送消息时间 + 服务器接收消息时间 + 服务器发送消息时间 - 设备接收时间 最后除于2 )
|
||
float deviceSendTime = doc["deviceSendTime"];
|
||
float serverSendTime = doc["serverSendTime"];
|
||
float serverRecvTime = doc["serverRecvTime"];
|
||
float deviceRecvTime = millis();
|
||
float now = (serverSendTime + serverRecvTime + deviceRecvTime - deviceSendTime) / 2;
|
||
// 过期时间 = 当前时间 + 1小时
|
||
float expireTime = now + 1 * 60 * 60 * 1000;
|
||
// 加密认证,密码格式为:mqtt密码 & 过期时间 & 授权码(可选),如果产品启用了授权码就必须加上
|
||
String password = "";
|
||
if (authCode == "")
|
||
{
|
||
password = (String)mqttPwd + "&" + String(expireTime, 0);
|
||
}
|
||
else
|
||
{
|
||
password = (String)mqttPwd + "&" + String(expireTime, 0) + "&" + authCode;
|
||
}
|
||
// 密码加密
|
||
printMsg("密码(未加密):" + password);
|
||
String encryptPassword = encrypt(password, mqttSecret, wumei_iv);
|
||
printMsg("密码(已加密):" + encryptPassword);
|
||
return encryptPassword;
|
||
}
|
||
|
||
/*
|
||
* 通过HTTP获取NTP时间
|
||
*/
|
||
String getTime()
|
||
{
|
||
while (WiFi.status() == WL_CONNECTED)
|
||
{
|
||
HTTPClient http;
|
||
printMsg("获取时间...");
|
||
if (http.begin(wifiClient, (ntpServer + (String)millis()).c_str()))
|
||
{
|
||
// 发送请求
|
||
int httpCode = http.GET();
|
||
if (httpCode > 0)
|
||
{
|
||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
|
||
{
|
||
printMsg("获取时间成功,data:");
|
||
Serial.print(http.getString());
|
||
return http.getString();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
printMsg("获取时间失败,error:");
|
||
Serial.printf(http.errorToString(httpCode).c_str());
|
||
}
|
||
http.end();
|
||
}
|
||
else
|
||
{
|
||
printMsg("连接Http失败");
|
||
}
|
||
delay(500);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* AES加密 (AES-CBC-128-pkcs5padding)
|
||
*/
|
||
String encrypt(String plain_data, char *wumei_key, char *wumei_iv)
|
||
{
|
||
int i;
|
||
// pkcs7padding填充 Block Size : 16
|
||
int len = plain_data.length();
|
||
int n_blocks = len / 16 + 1;
|
||
uint8_t n_padding = n_blocks * 16 - len;
|
||
uint8_t data[n_blocks * 16];
|
||
memcpy(data, plain_data.c_str(), len);
|
||
for (i = len; i < n_blocks * 16; i++)
|
||
{
|
||
data[i] = n_padding;
|
||
}
|
||
uint8_t key[16], iv[16];
|
||
memcpy(key, wumei_key, 16);
|
||
memcpy(iv, wumei_iv, 16);
|
||
// 加密
|
||
br_aes_big_cbcenc_keys encCtx;
|
||
br_aes_big_cbcenc_init(&encCtx, key, 16);
|
||
br_aes_big_cbcenc_run(&encCtx, iv, data, n_blocks * 16);
|
||
// Base64编码
|
||
len = n_blocks * 16;
|
||
char encoded_data[base64_enc_len(len)];
|
||
base64_encode(encoded_data, (char *)data, len);
|
||
return String(encoded_data);
|
||
}
|
||
|
||
/*
|
||
* AES解密 (AES-CBC-128-pkcs5padding)
|
||
*/
|
||
String decrypt(String encoded_data_str, char *wumei_key, char *wumei_iv)
|
||
{
|
||
int input_len = encoded_data_str.length();
|
||
char *encoded_data = const_cast<char *>(encoded_data_str.c_str());
|
||
int len = base64_dec_len(encoded_data, input_len);
|
||
uint8_t data[len];
|
||
base64_decode((char *)data, encoded_data, input_len);
|
||
uint8_t key[16], iv[16];
|
||
memcpy(key, wumei_key, 16);
|
||
memcpy(iv, wumei_iv, 16);
|
||
int n_blocks = len / 16;
|
||
br_aes_big_cbcdec_keys decCtx;
|
||
br_aes_big_cbcdec_init(&decCtx, key, 16);
|
||
br_aes_big_cbcdec_run(&decCtx, iv, data, n_blocks * 16);
|
||
// PKCS#7 Padding 填充
|
||
uint8_t n_padding = data[n_blocks * 16 - 1];
|
||
len = n_blocks * 16 - n_padding;
|
||
char plain_data[len + 1];
|
||
memcpy(plain_data, data, len);
|
||
plain_data[len] = '\0';
|
||
return String(plain_data);
|
||
}
|