具体操作
- 本实验采用 本地环回测试
- 开启wireshark抓包工具,设定端口号 tcp.port == 5099 (5099为服务端对外开启服务的端口号),不可以使用ip.addr指定ip地址,因为本地环回测试,相关信息太多,使用端口抓包最为简单
- 首先开启服务端,然后开启客户端,服务端开启之后输出 "Bind returned success",并输出客户端连接通信使用的端口号,然后客户端会使用socket通信传输经过SM4对称加密算法加密返回的密文
- 密文存储格式是字符串,表示形式是十六进制;socket通信的时候会将这个字符串再次编码为十六进制,因此使用wireshark抓包数据之后 需要对拦截的数据进行解码 (十六进制 转 字符串)
- hex转str - 在线工具
- 客户端发送数据完毕之后,输出Client:sending data test,请按任意键退出
- 服务端打印输出 客户端发送数据的数据 以及对应的长度等信息,输出 Connect closed,结束通信
服务端
客户端
wireshark抓包
- 客户端 端口号 向 服务端 端口号 发送33字节数据
- 点击 Data,查看下窗口,左边是数据的十六进制,右边是字符串
- 将wireshark抓到的数据和server接收到的数据对比,数据一致
- 表明客户端和发送端数据传输是经过SM4算法加密的
参考链接
- https://blog.csdn.net/CHYabc123456hh/article/details/122049078
- SM4 ECB加密模式 数据对比试验论证_CHYabc123456hh的博客-CSDN博客
- 数据填充规则之PKCS7_CHYabc123456hh的博客-CSDN博客
- C++ Byte转十六进制字符串输出_CHYabc123456hh的博客-CSDN博客
- 使用国密浏览器和使用Wireshark进行国密抓包_CHYabc123456hh的博客-CSDN博客_gmssl wireshark
- Socket代码实现服务端 和 客户端之间通信_CHYabc123456hh的博客-CSDN博客_c++实现客户端socket
- Socket编程 涵盖代码和函数参数介绍_CHYabc123456hh的博客-CSDN博客
- 本地搭建server和客户端使用端口进行数据通信,使用Wireshark抓取127.0.0.1环回地址并分析通信数据_CHYabc123456hh的博客-CSDN博客
- 验证客户端和服务端可以传输经SM4加密的密文数据,从而验证发送数据已使用服务器密码机进行SM4加密,而不是随便的字符串乱码_CHYabc123456hh的博客-CSDN博客
服务端代码
// sdf_cpp_warpper.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// server端#ifndef UNICODE
#define UNICODE
#endif#define WIN32_LEAN_AND_MEAN#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>#include "sdf_warpper.hpp"
#include "cstring"#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib,"libhsm_core.lib")#define DEFAULT_BUFLEN 1024
using namespace std;typedef std::vector<unsigned char> bytes;
std::string BytesToStr(const bytes& in)
{bytes::const_iterator from = in.cbegin();bytes::const_iterator to = in.cend();std::ostringstream oss;for (; from != to; ++from)oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(*from);return oss.str();
}void sm4_encrypt_decrypt_demo() {sdf_qax::SDF_warpper sdf_warpper("{\"device_type\": \"rpc\",\"device_socket\": \"172.22.14.231:5000\"}");//char a[16] = { '1','0','0','0','0','2','4','5','6','7','2','1','3','4','5','6' };char a[]{ '0','0','0','0','0','0','0','0','0','0','0','0','0','0' };//char a[]{ 00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };//std::cout << "input_data: ";std::cout << "char类型数据: ";
/* for (auto i : a) {std::cout << i << " ";} *///std::cout << std::endl; bytes input(a, a + 15);for (auto i : input) {std::cout << i;} std::cout << std::endl;std::cout << "char类型数据 转十六进制 输出: ";std::cout << BytesToStr(input) << std::endl; char outbuffer[32]{ 0 };int out_buffer_true_length = 0;sdf_warpper.sm4_symmetry_encrypt((uint8_t *)sdf_warpper.getStaticKey(), a, sizeof(a) / sizeof(char), outbuffer, &out_buffer_true_length);std::cout << "cipher data with padding length: " << out_buffer_true_length << std::endl;char static_key[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };bytes input_3(static_key, static_key + 15);std::cout << "static_key数据 转十六进制 输出: ";std::cout << BytesToStr(input_3) << std::endl;/* for (char i : outbuffer) {std::cout << i;} */bytes input_2(outbuffer, outbuffer + out_buffer_true_length);std::cout << "密文数据输出:";std::cout << BytesToStr(input_2) << std::endl;//std::cout << std::endl;std::cout << "true array length: " << strlen(outbuffer) << std::endl;char decrypt_result[32]{ 0 };int out_true_length = 0;sdf_warpper.sm4_symmetry_decrypt((uint8_t *)sdf_warpper.getStaticKey(), outbuffer, out_buffer_true_length, decrypt_result, &out_true_length);std::cout << "decrypt result data: ";for (int i = 0; i < strlen(decrypt_result); i++) {std::cout << decrypt_result[i];}std::cout << std::endl;
}ECCCipher cipher; //密文存储结构体void sm2_encrypt_decrypt_demo() {sdf_qax::SDF_warpper sdf_warpper2("{\"device_type\": \"rpc\",\"device_socket\": \"172.22.14.231:5000\"}");char a[]{ '3','4','5' };std::cout << "input_data: ";for (char i : a) {std::cout << i;}std::cout << std::endl;sdf_warpper2.sm2_internal_encrypt(1, a, 3, &cipher);char decrypt_result[256]{ 0 };int out_true_length = 0;sdf_warpper2.sm2_internal_decrypt(1, &cipher, reinterpret_cast<sdf_uint8_t *>(decrypt_result), &out_true_length, nullptr, 0);std::cout << "Data length after decryption: " << out_true_length << std::endl;std::cout << "Data after decryption: ";for (int i = 0; i < out_true_length; i++) {std::cout << decrypt_result[i];}std::cout << std::endl;
}void main()
{//Initialze winsockWSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){std::cout << "Can't Initialize winsock!Quiting!" << std::endl;;return;}//Creste a sockrtSOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);if (sockSrv == INVALID_SOCKET) {wprintf(L"Can't create a socket with error %d\n", WSAGetLastError());WSACleanup();return;}//Bind the socket to an ip address and portint port = 5099;SOCKADDR_IN addrSrv;addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(port); //1024以上的端口号addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//Bind the socketint retVal = bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));if (retVal == SOCKET_ERROR) {printf("Failed bind:%d\n", WSAGetLastError());closesocket(sockSrv);WSACleanup();return;}else {std::cout << "Bind returned success" << std::endl;}//Tell winsock the socket is for listeningif (listen(sockSrv, SOMAXCONN) == SOCKET_ERROR) {printf("Listen failed:%d", WSAGetLastError());return;}//wait for a connectionSOCKADDR_IN addrClient;int clientSize = sizeof(addrClient);SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &clientSize);if (sockConn == SOCKET_ERROR) {wprintf(L"accept failed with error: %ld\n", WSAGetLastError());closesocket(sockSrv);WSACleanup();return;}char host[NI_MAXHOST]; //Client's remote namechar service[NI_MAXSERV]; //Service (i.e. port)the client is connect onZeroMemory(host, NI_MAXHOST);//Same as memset(host,0,NI_MAXHOST)ZeroMemory(service, NI_MAXSERV);if (getnameinfo((sockaddr*)&addrClient, sizeof(addrClient), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) {std::cout << "connect on port " << service << std::endl;}else {inet_ntop(AF_INET, &addrClient.sin_addr, host, NI_MAXHOST);std::cout << host << " connect on port " << ntohs(addrClient.sin_port) << std::endl;}//while loop:accept and echo message back to clientchar recvbuf[DEFAULT_BUFLEN] = "";int recvbuflen = DEFAULT_BUFLEN;memset(recvbuf, 0, sizeof(recvbuf));char sendbuf[] = "Client: sending data test\n";retVal = send(sockConn, sendbuf, sizeof(sendbuf), 0);if (retVal == SOCKET_ERROR) {wprintf(L"send failed with error: %d\n", WSAGetLastError());closesocket(sockSrv);WSACleanup();return;}do {retVal = recv(sockConn, recvbuf, recvbuflen, 0);if (retVal > 0) {wprintf(L"Bytes received: %d\n", retVal);printf("%s\n", recvbuf);}else if (retVal == 0)wprintf(L"Connection closed\n");elsewprintf(L"recv failed with error: %d\n", WSAGetLastError());} while (retVal > 0);closesocket(sockConn);//close socketclosesocket(sockSrv);//close winsockWSACleanup();system("pause");
}// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
客户端代码
// sdf_cpp_warpper.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// client端#ifndef UNICODE
#define UNICODE
#endif#define WIN32_LEAN_AND_MEAN#include <iostream>
#include <stdio.h>
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>
#include <winsock2.h>
#include <Ws2tcpip.h>#include "sdf_warpper.hpp"
#include "cstring"#pragma comment(lib,"libhsm_core.lib")
#pragma comment(lib, "Ws2_32.lib")
using namespace std;typedef std::vector<unsigned char> bytes;
std::string BytesToStr(const bytes& in)
{bytes::const_iterator from = in.cbegin();bytes::const_iterator to = in.cend();std::ostringstream oss;for (; from != to; ++from)oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(*from);return oss.str();
}void sm4_encrypt_decrypt_demo(char* outbuffer,int & out_buffer_true_length) {sdf_qax::SDF_warpper sdf_warpper("{\"device_type\": \"rpc\",\"device_socket\": \"172.22.14.231:5000\"}");//char a[16] = { '1','0','0','0','0','2','4','5','6','7','2','1','3','4','5','6' };char a[]{ '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0' };//char a[]{ 00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };//std::cout << "input_data: ";//std::cout << "char类型数据: ";/* for (auto i : a) {std::cout << i << " ";} *///std::cout << std::endl; bytes input(a, a + 15);
/* for (auto i : input) {std::cout << i;}std::cout << std::endl;std::cout << "char类型数据 转十六进制 输出: ";std::cout << BytesToStr(input) << std::endl; *///char outbuffer[32]{ 0 };//int out_buffer_true_length = 0;sdf_warpper.sm4_symmetry_encrypt((uint8_t*)sdf_warpper.getStaticKey(), a, sizeof(a) / sizeof(char), outbuffer, &out_buffer_true_length);
// std::cout << "cipher data with padding length: " << out_buffer_true_length << std::endl;char static_key[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };bytes input_3(static_key, static_key + 15);
/* std::cout << "static_key数据 转十六进制 输出: ";std::cout << BytesToStr(input_3) << std::endl; *//* for (char i : outbuffer) {std::cout << i;} */bytes input_2(outbuffer, outbuffer + out_buffer_true_length);//std::cout << "密文数据输出:";//std::cout << BytesToStr(input_2) << std::endl;//std::cout << std::endl;
// std::cout << "true array length: " << strlen(outbuffer) << std::endl;char decrypt_result[32]{ 0 };int out_true_length = 0;sdf_warpper.sm4_symmetry_decrypt((uint8_t*)sdf_warpper.getStaticKey(), outbuffer, out_buffer_true_length, decrypt_result, &out_true_length);//std::cout << "decrypt result data: ";
/* for (int i = 0; i < strlen(decrypt_result); i++) {std::cout << decrypt_result[i]; } */std::cout << std::endl;
}
ECCCipher cipher; //密文存储结构体void sm2_encrypt_decrypt_demo() {sdf_qax::SDF_warpper sdf_warpper2("{\"device_type\": \"rpc\",\"device_socket\": \"172.22.14.231:5000\"}"); char a[]{ '3','4','5' };std::cout << "input_data: ";for (char i : a) {std::cout << i;}std::cout << std::endl;sdf_warpper2.sm2_internal_encrypt(1, a, 3, &cipher);char decrypt_result[256]{ 0 };int out_true_length = 0;sdf_warpper2.sm2_internal_decrypt(1, &cipher, reinterpret_cast<sdf_uint8_t *>(decrypt_result), &out_true_length, nullptr, 0);std::cout << "Data length after decryption: " << out_true_length << std::endl;std::cout << "Data after decryption: ";for (int i = 0; i < out_true_length; i++) {std::cout << decrypt_result[i];}std::cout << std::endl;
}void main() {//Initialze winsockWSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){std::cout << "Can't Initialize winsock!Quiting!" << std::endl;;return;}//Creste a sockrtSOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);if (sockClient == INVALID_SOCKET) {wprintf(L"Can't create a socket with error %d\n", WSAGetLastError());WSACleanup();return;}//Bind the socket to an ip address and portint port = 5099;SOCKADDR_IN addrSrv;addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(5099);//addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");inet_pton(AF_INET, "127.0.0.1", &addrSrv.sin_addr.S_un.S_addr);char buff[1024];memset(buff, 0, sizeof(buff));//向服务器发出连接请求if (connect(sockClient, (struct sockaddr*) & addrSrv, sizeof(addrSrv)) == INVALID_SOCKET) {printf("Connect failed:%d", WSAGetLastError());return;}else{//接收数据recv(sockClient, buff, sizeof(buff), 0);printf("%s\n", buff);}//发送数据//const char* buffSend = "hello, this is a Client....";//send(sockClient, buffSend, strlen(buffSend) + 1, 0);// char sendbuf[BUFSIZ];
// ZeroMemory(sendbuf, BUFSIZ);
/* strcpy_s(sendbuf, str.c_str());if (send(sockClient, sendbuf, strlen(sendbuf) + 1, 0) == SOCKET_ERROR) {wprintf(L"send failed with error: %d\n", WSAGetLastError());closesocket(sockClient);WSACleanup();break;}*/char outbuffer[32]{ 0 };int out_buffer_true_length = 0;sm4_encrypt_decrypt_demo(outbuffer,out_buffer_true_length);bytes input_2(outbuffer, outbuffer + out_buffer_true_length);//std::cout << "密文数据输出:";//std::cout << BytesToStr(input_2) << std::endl;//printf("%d", strlen(buffSend) + 1);std::string return_value(BytesToStr(input_2));//std::cout << return_value << std::endl;char sendbuf[BUFSIZ];ZeroMemory(sendbuf, BUFSIZ);strcpy_s(sendbuf, return_value.c_str());if (send(sockClient, sendbuf, strlen(sendbuf) + 1, 0) == SOCKET_ERROR) {wprintf(L"send failed with error: %d\n", WSAGetLastError());closesocket(sockClient);WSACleanup();exit(-1);}//关闭套接字closesocket(sockClient);WSACleanup();system("pause");}// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件