平台SSL协议
一、协议部分
SSL_AsyncRegCallback注册回调
http_SecurityRecvDataCllback加密成功回调
http_SecuritySendDataCllback解密成功回调
http_SecurityPostMessageCllback握手信息回调
http_SecurityShowCertInfoCllback证书错误回调
二、工作流程分析
由上图的协议可以,在发送、接收数据之前,都要先通知SSL层。成功之后再走正常的Socket收发流程
例如发送数据的流程(接收同理)
Soket的处理在我的文章展讯6531平台socket中有详细的说明,这里我们只说差异的部分,根据第一部分的SSL流程的分析。在收发数据之前都需要通知调用SSL的加密解密。然后通过SSL的回调在进行密文的收发。流程如下
1、sci_sock_connect之后建立SSL:SSL_Create(源码在HTTP_SslInit),并且给SSL注册上层回调SSL_AsyncRegCallback(源码在httpHandshaking)
2、线程内监听socket事件的到来
socket可写:SSL_EncryptPasser加密数据,SSL加密成功之后调用我们注册的回调http_SecuritySendDataCllback,之后处理逻辑,我这里直接sci_sock_send,并且通知SSL:SSL_AsyncMessageProc(((HX_SALE_SSL_INSTANCE *)httpmachine_ptr)->hSSl, SSL_RECV_MESSAGE_SEND_SUCC, data_len);我发送成功了,释放占用的资源吧
socket可读:sci_sock_recv之后,此时的数据还是密文状态,需要调用SSL接口解密数据:SSL_DecryptPasser,SSL解密成功之后调用回调:http_SecurityRecvDataCllback,之后就是对接收的数据操作了,我是直接解析数据的内容,然后SSL_AsyncMessageProc(((HX_SALE_SSL_INSTANCE *)httpmachine_ptr)->hSSl, SSL_RECV_MESSAGE_RECV_SUCC, data_len);,通知SSL释放资源。
至此,SSL的处理流程完毕。只是在HTTP的基础上加上SSL的处理,就能调用HTTPS的接口收发数据了
SSL_EncryptPasser(SslInstance->hSSl, buf, strlen(buf));
|
|
http_SecuritySendDataCllback
|
|
http_Sending
|
|
sci_sock_send
|
|
SSL_AsyncMessageProc(((HX_SALE_SSL_INSTANCE *)httpmachine_ptr)->hSSl, SSL_RECV_MESSAGE_SEND_SUCC, data_len);//发送成功之后,需要向SSL发送异步消息。通知SSL释放占用的资源
三、源码
部分实现源码如下:
/*****************************************************************************/
// Description : 封装JSON格式的数据
// Global resource dependence : none
// Author: jiemin lai
// Note: HX_SALE_SEND_DATA_TYPE
/*****************************************************************************/
LOCAL uint8 * MMISALE_CreateJSONpack(HX_SALE_SEND_DATA_TYPE CustomData)
{
cJSON *root = PNULL;
char *ret_ptr = PNULL;
char plmn_info_data[8] = {0};
SCI_TRACE_LOW("MMISALE_CreateJSONpack ...entry...");
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "IMEI1", cJSON_CreateString(CustomData.imei1));
cJSON_AddItemToObject(root, "IMEI2", cJSON_CreateString(CustomData.imei2));
cJSON_AddItemToObject(root, "model", cJSON_CreateString(CustomData.model));
cJSON_AddItemToObject(root, "modelDetail", cJSON_CreateString(CustomData.modelDetail));
cJSON_AddItemToObject(root, "currentVersion", cJSON_CreateString(CustomData.currentVersion));
cJSON_AddItemToObject(root, "serialNumber", cJSON_CreateString(CustomData.serialNumber));
sprintf(plmn_info_data, "%d%02d", CustomData.sim1plmn.mcc, CustomData.sim1plmn.mnc);
cJSON_AddItemToObject(root, "operator", cJSON_CreateString(plmn_info_data));
memset(plmn_info_data, 0, sizeof(plmn_info_data));
sprintf(plmn_info_data, "%d%02d", CustomData.sim2plmn.mcc, CustomData.sim2plmn.mnc);
cJSON_AddItemToObject(root, "operator2", cJSON_CreateString(plmn_info_data));
cJSON_AddItemToObject(root, "language", cJSON_CreateString(CustomData.language));
ret_ptr = cJSON_Print(root);
cJSON_Delete(root);
//free(root);
SCI_TRACE_LOW("MMISALE_CreateJSONpack ...out...");
return ret_ptr;
}
/*****************************************************************************/
// Description : 解析服务器返回的数据
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
LOCAL BOOLEAN HxSaleParseDataInfo(char* CustomData)
{
char* b = PNULL;
char* e = PNULL;
char RecvData[8] = {0};
SCI_TRACE_LOW("HxSaleParseDataInfo Entry !");
if(PNULL == CustomData)
{
SCI_TRACE_LOW("HxSaleParseDataInfo CustomData == PNULL!");
return FALSE;
}
if( MMIAPICOM_Stristr(CustomData," 200") == NULL) //不是返回的200,那说明是错误的
{
SCI_TRACE_LOW("HxSaleParseDataInfo (server data:HTTP/1.1 200) no data");
return FALSE;
}
#if defined(TP_LINK_SALES_STATISTICS)&&defined(WIN32)//激活次数统计
Sales_start_flag_add();
if(TP_LINK_Sales_Start_Flag_Times()<5)
{
MMIDefault_StartSalesTimer();
return FALSE;
}
#endif
//成功的标志是{"type":"success"}
e = b = MMIAPICOM_Stristr(CustomData,"{\"type\":\"") + strlen("{\"type\":\""); //到前一个分号
e = MMIAPICOM_Stristr(b, "\""); //到后一个分号,e跟b之间就是返回的数据
strncpy(RecvData, b, e - b);
SCI_TRACE_LOW("HxSaleParseDataInfo RecvData=:%s", RecvData);
if((strlen(RecvData) != 0) && (0 == strncmp("success", RecvData, strlen(RecvData)))) // 表示成功,服务器已接收请求
{
//清除销量统计的NV
#if defined(TP_LINK_SALES_STATISTICS)
sales_statistics_send_success();
MMIIDefault_StopSalesTimer();
#endif
SCI_TRACE_LOW("HxSaleParseDataInfo parse custom data success !");
return TRUE;
}
}
/*****************************************************************************/
// Description : 向socket线程发送信号(socket打开过程中遇到错误)
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
LOCAL BOOLEAN HxSaleSendSignal(void)
{
xSignalHeaderRec *sig_ptr = 0;
SCI_TRACE_LOW("HxSaleSendSignal");
sig_ptr = (xSignalHeaderRec *)SCI_ALLOCA(sizeof(xSignalHeaderRec));
SCI_ASSERT(sig_ptr);/*assert verified*/
sig_ptr->SignalCode = MSG_SALE_SOCKET_ERROR;
sig_ptr->SignalSize = sizeof(xSignalHeaderRec);
sig_ptr->Sender = SCI_IdentifyThread();
SCI_SendSignal((xSignalHeader)sig_ptr, s_sale_task_id);
}
/*****************************************************************************/
// Description :销量统计条件达到,调用此函数执行网络端发送,本模块实现,外部模块调用
// Global resource dependence : none
// Author: jiemin lai
// Note: HX_SALE_SEND_DATA_TYPE
/*****************************************************************************/
PUBLIC BOOLEAN MMISALE_RunSendInfor(void)
{
#if defined(TP_LINK_SALES_STATISTICS)
if(TP_LINK_Sales_Start_Flag_Times()<5)
#endif
{
#ifdef WIN32
char testbuf[]="HTTP/1.1 200 Date: Tue, 19 Feb 2019 10:28:54 GMT Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Connection: close Server: nginx/1.12.1 12 {\"type\":\"success\"}";
HxSaleParseDataInfo(testbuf);
#else
MMIAPIPDP_Deactive(MMI_MODULE_SALE);
HxSaleActivePDP();
#endif
}
}
#ifdef HTTPS_SUPPORT
/*****************************************************************************/
// Description : 加密数据成功的回调
// Global resource dependence : none
// Author: jiemin lai
// Note: httpmachine_ptr SSL句柄 data_ptr加密后的数据 data_len加密后的数据长度
/*****************************************************************************/
int32 http_SecuritySendDataCllback( void *httpmachine_ptr, uint8 *data_ptr, uint32 data_len)
{
SCI_TRACE_LOW("http_SecuritySendDataCllback is run!");
if((PNULL == httpmachine_ptr) || (PNULL == data_ptr) || (0 == data_len))
{
SCI_TRACE_LOW("http_SecuritySendData parameters fail!");
return 0;
}
#if 0
BackBuf = SCI_MALLOC(uint8 *, data_len * sizeof(uint8));
SCI_MEMCPY(BackBuf, data_ptr, data_len);
//获取数据
HTTP_SslGetRequestData(buf);
//SSL_DecryptPasser((HX_SALE_SSL_INSTANCE *)httpmachine_ptr->hSSL, buf, strlen(buf));
//加密
SSL_EncryptPasser((HX_SALE_SSL_INSTANCE *)httpmachine_ptr->hSSL, buf, strlen(buf));
return_ret = sci_sock_send((HX_SALE_SSL_INSTANCE *)httpmachine_ptr->fd, buf, strlen(buf), 0);
SCI_TRACE_LOW("http_SecuritySendData send ret = :%d", return_ret);
#endif
//加密成功了就调用发送接口
http_Sending(httpmachine_ptr, data_ptr, data_len);
//SSL_AsyncMessageProc((HX_SALE_SSL_INSTANCE *)httpmachine_ptr->hSSL, SSL_RECV_MESSAGE_SEND_SUCC);
}
/*****************************************************************************/
// Description : 解密数据成功的回调
// Global resource dependence : none
// Author: jiemin lai
// Note: httpmachine_ptr SSL句柄 data_ptr解密后的数据 data_len解密后的数据长度
/*****************************************************************************/
void http_SecurityRecvDataCllback(void *httpmachine_ptr, uint8 *data_ptr, uint32 data_len)
{
SCI_TRACE_LOW("http_SecurityRecvDataCllback is run!");
if((PNULL == httpmachine_ptr) || (PNULL == data_ptr) || (0 == data_len))
{
SCI_TRACE_LOW("http_SecurityRecvData parameters fail!");
return ;
}
http_Receiveing(httpmachine_ptr, data_ptr, data_len);
}
/*****************************************************************************/
// Description : 传递加密解密数据以及握手过程中可能出现的问题
// Global resource dependence : none
// Author: jiemin lai
// Note: void *httpmachine_ptr句柄, uint32 message_id消息ID
/*****************************************************************************/
void http_SecurityPostMessageCllback(void *httpmachine_ptr, uint32 message_id)
{
SCI_TRACE_LOW("http_SecurityPostMessageCllback is run!");
if(PNULL == httpmachine_ptr)
{
SCI_TRACE_LOW("http_SecurityPostMessage parameters fail");
return ;
}
switch (message_id)
{
//case SECURITY_HANDSHAKE_SUCC:
case SSL_SEND_MESSAGE_HANDSHAKE_SUCC: // 握手成功
{
//http handle this case according to its state
SCI_TRACE_LOW("http_SecurityPostMessage SSL_SEND_MESSAGE_HANDSHAKE_SUCC");
}
break;
case SSL_SEND_MESSAGE_FAIL: //发送失败
{
//http handle this case according to its state
SCI_TRACE_LOW("http_SecurityPostMessage SSL_SEND_MESSAGE_FAIL");
}
break;
//case SSL_RECV_MESSAGE_SEND_SUCC:
//SSL_EncryptPasser(SSL_HANDLE ssl_handle, uint8 * data_ptr, uint32 len)
//break;
//case SSL_RECV_MESSAGE_RECV_SUCC: //接收成功
//SSL_DecryptPasser((HX_SALE_SSL_INSTANCE *)httpmachine_ptr->hSSL, RecvBuf, RecvDateLenn);
//break;
case SSL_SEND_MESSAGE_CLOSE_BY_SERVER: //服务器发送关闭
{
SCI_TRACE_LOW("http_SecurityPostMessage SSL_SEND_MESSAGE_CLOSE_BY_SERVER");
}
break;
case SSL_SEND_MESSAGE_CANCLED_BY_USER: // 证书出现问题
{
//http handle this case according to its state
SCI_TRACE_LOW("http_SecurityPostMessage SSL_SEND_MESSAGE_CANCLED_BY_USER");
}
break;
default:
SCI_TRACE_LOW("http_SecurityPostMessage default");
break;
}
}
/*****************************************************************************/
// Description : 当证书无法验证时,需要将将需要确认的消息传递给应用协议
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
void http_SecurityShowCertInfoCllback(void *machine_ptr, uint8 *title_ptr, uint8 *info_ptr)
{
SCI_TRACE_LOW("http_SecurityShowCertInfoCllback is run!");
if((PNULL == machine_ptr) || (PNULL == title_ptr) || (PNULL == info_ptr))
{
SCI_TRACE_LOW("http_SecurityShowCertInfo parameters fail");
return ;
}
//通知SSL。停止
SSL_UserCnfCert(((HX_SALE_SSL_INSTANCE *)machine_ptr)->hSSl , SSL_ASYNC);
}
/*****************************************************************************/
// Description : 建立SSL的安全连接
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
void* httpHandshaking(void* machine_ptr)
{
SSL_CALLBACK_T astCbFun = {0};
struct sci_sockaddr addr;
struct sci_hostent * hostent_date = PNULL;
uint32 ip = 0;
uint32 hostip = 0;
uint16 port = SALE_SERVICE_PORT;
char *addr_str = NULL;
SCI_TRACE_LOW("httpHandshaking is run!");
astCbFun.decryptout_cb = http_SecurityRecvDataCllback;
astCbFun.encryptout_cb = http_SecuritySendDataCllback;
astCbFun.postmessage_cb = http_SecurityPostMessageCllback;
astCbFun.showcert_cb = http_SecurityShowCertInfoCllback;
hostent_date = sci_gethostbyname(g_sale_domain);
SCI_MEMCPY(&hostip, hostent_date->h_addr_list[0], 4);
//ntohl(ip);
ip = ntohl(hostip);
addr.family = AF_INET;
//addr.ip_addr = htonl(ip);
addr.ip_addr = htonl(ip);
addr.port = htons(port);
//memset(addr.sa_data, 0, 8 * sizeof(char));
SCI_MEMSET((void*)addr.sa_data, 0, 8*sizeof(char));
addr_str = inet_ntoa(addr.ip_addr);
SSL_AsyncRegCallback(((HX_SALE_SSL_INSTANCE *)machine_ptr)->hSSl, &astCbFun);
SSL_ProtocolChoose(((HX_SALE_SSL_INSTANCE *)machine_ptr)->hSSl,SSL_PROTOCOLBOTH,SSL_ASYNC); //两个版本都支持
SSL_HandShake(((HX_SALE_SSL_INSTANCE *)machine_ptr)->hSSl, addr_str, SALE_SERVICE_PORT, SSL_ASYNC);
}
/*****************************************************************************/
// Description : 发送数据
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
void* http_Receiveing(void *httpmachine_ptr, uint8 *data_ptr, uint32 data_len)
{
SCI_TRACE_LOW("http_Receiveing is run!");
if((PNULL == httpmachine_ptr) || (PNULL == data_ptr) || (0 == data_len))
{
SCI_TRACE_LOW("http_Receiveing parameters fail");
return PNULL ;
}
SCI_TRACE_LOW("http_Receiveing data_ptr:%s", data_ptr);
HxSaleParseDataInfo(data_ptr);
}
/*****************************************************************************/
// Description : 接收数据
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
void* http_Sending(void *httpmachine_ptr, uint8 *data_ptr, uint32 data_len)
{
int return_ret = 0;
SCI_TRACE_LOW("http_Sending is run!");
if((PNULL == httpmachine_ptr) || (PNULL == data_ptr) || (0 == data_len))
{
SCI_TRACE_LOW("http_Sending parameters fail");
return PNULL ;
}
return_ret = sci_sock_send(((HX_SALE_SSL_INSTANCE *)httpmachine_ptr)->fd, data_ptr, data_len, 0);
SCI_TRACE_LOW("http_SecuritySendData send ret = :%d", return_ret);
if(return_ret > 0)
{
//发送成功,给上层发送消息,释放SSL的资源
SSL_AsyncMessageProc(((HX_SALE_SSL_INSTANCE *)httpmachine_ptr)->hSSl, SSL_RECV_MESSAGE_SEND_SUCC, data_len);
}
}
/*****************************************************************************/
// Description : 创建SSL状态机
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
void* http_CreateSSL(int socket_id)
{
SCI_TRACE_LOW("http_Sending is run!");
SslInstance = (HX_SALE_SSL_INSTANCE *)malloc(sizeof(HX_SALE_SSL_INSTANCE));
if (PNULL == SslInstance)
{
return NULL;
}
SCI_MEMSET(SslInstance,0,sizeof(HX_SALE_SSL_INSTANCE));
SslInstance->fd = socket_id;
SslInstance->hSSl = SSL_Create(SslInstance, socket_id, SSL_ASYNC);
return SslInstance;
}
/*****************************************************************************/
// Description : 后去需要加密的发送数据
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
void HTTP_SslGetRequestData(char* RequestData)
{
char json[256] = {0};
HX_SALE_SEND_DATA_TYPE CustomBuffer = {0};
SCI_TRACE_LOW("HTTP_SslGetRequestData is run!");
MMISALE_GetCustomInfo(&CustomBuffer);
if(PNULL == RequestData)
{
SCI_TRACE_LOW("HTTP_SslGetRequestData parameter fail!");
return;
}
sprintf(RequestData,"POST https://%s/fota-open-ota/phone/rom/active HTTP/1.1\r\nHost: %s\r\nConnection: close\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %d\r\nAccept: */*\r\n\r\n%s\r\n", g_sale_domain, g_sale_domain, strlen(json), json);
}
/*****************************************************************************/
// Description : SSL初始化
// Global resource dependence : none
// Author: jiemin lai
// Note:
/*****************************************************************************/
void HTTP_SslInit(int socket_id)
{
SCI_TRACE_LOW("HTTP_SslInit is run!");
http_CreateSSL(socket_id);
httpHandshaking((void *)SslInstance);
}
#endif
四、HTTPS调试遇到的问题
1、展讯 SC6531E\SC7703平台只支持SSL 3.0 TLS1.0版本。如果服务器的加密版本高于此,在在HTTP的四次握手完成之后的SSL握手中,将会拒绝我们的握手请求。报文如下(Wireshark分析)
2、HTTPS请求被拒绝
HTTPS的端口号默认是443、HTTP是80,在调试完HTTP之后切换HTTPS时,由于没有修改端口号,导致握手请求在发起第一次握手时就被拒绝。实在CSDN的大佬帖子上看到的,其中一个原因就是端口号不对。找不到帖子地址了。