壹、设计需求

TPM程序设计基础:
可信平台模块(TPM)是当今计算机中安装率越来越高的一个模块。其作用是通过提供一些密码学功能来提高系统的安全性。本项目主要是对其的简单应用实践,目标是编程撰写一个对系统硬件环境进行验证或安全通信的程序。

贰、环境要求|前置

1、首先要确保你的Visual Studio 2022是最新版本的,everything搜索Visual Studio Install。

2、然后进入提供的项目,正常一打开就会提示是否更新,更新就是了。
3、首先你得把这个项目跑起来,Ctrl+b后触发“生成”(Build)命令,将项目编译成可执行文件,编译成功会生成一个bin目录,建议是调成32位的吧


随后在确保开启了tpm模拟环境(Simulator.exe)运行即可。

这样就成功跑起来了,接下来就是我对于此项目的一些改动以及一些思路想法

叄、基于hash实现c-s的通讯安全性检测

主要思路(莫超,否则百分百哦):
假设客户端是发送方,服务器端是接收方,那么客户端得把消息以及自消息摘要一起发送。服务器端接收到后,得把收到的消息做hash,再和发送过来的摘要对比,因为hash的雪崩效应,即使消息被篡改了小部分,摘要也会有很大的区别,这才知道消息是否完整。

1、环境配置

为什么这里又要进行环境配置?
因为之前只是为了跑而配置,现在是为了基于hash实现cs的通讯安全性检测而配置(看个人也可以不配置)。
这里给出我的一些配置其实就是导入库hh(右键’TSS.CPP Samples’这个项目->属性):



虽然说TSS.CPP\CryptoEngine\openssl\VC-WIN32这个路径也存在openssl库(版本好像比较高),但是我是以1.1版本实现的

2、代码层面

针对Sample.h

1)、将中定义hash函数略作修改,具体修改为:

1
void Hash(const std::string host,int PORT);

新增了两个参数,一个是服务器端的IP地址,另一个是服务器端的开放监听端口,主要是方便配置服务器端,使客户端能够成功连接。

针对Samples.cpp

首先把除实现hash之外的函数全部注释

1)、在void Samples::RunAllSamples()中,实现调用Hash函数。

1
2
_check;
Hash("192.168.33.1",6666);

2)、引入以下头文件

1
2
3
4
5
6
7
8
#include "stdafx.h"
#include "Samples.h"
#include "TpmConfig.h"
#include <openssl/sha.h>
#include <sys/types.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;

3)、修改Hash()的代码(仅供参考,切勿cv,不然查重率ber分ber)
主要就是这个代码,琢磨了好久,你知道的:gpt仅仅只是给出代码,但是能不能实现你自己的需求,关键是看自己。所以这里建议根据我的或gpt的代码加入自己的东西,真的会受益匪浅
好了别废话了,以下是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
void Samples::Hash(const std::string host, int PORT)
{
Announce("Hash");

std::cout << "Starting server..." << std::endl;//输出等待客户端连接

int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) {//服务器创建了一个套接字 (socket),用于监听客户端连接。如果创建失败,将输出错误消息并退出程序。
std::cerr << "Failed to create server socket." << std::endl;
return;
}
//这几行设置服务器的地址信息。sockaddr_in 结构体用于表示IPv4地址,AF_INET 表示使用IPv4协议。INADDR_ANY 表示服务器将监听所有可用的网络接口。
sockaddr_in serverAddress{};
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(PORT);

if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
std::cerr << "Failed to bind server socket." << std::endl;
//绑定套接字到指定的地址和端口。如果绑定失败,将输出错误消息并退出程序。
return;
}

if (listen(serverSocket, 5) == -1) {
std::cerr << "Failed to listen on server socket." << std::endl;
//开始监听客户端连接。如果监听失败,将输出错误消息并退出程序。
return;
}

while (true) {//循环,等待客户端连接。一旦有客户端连接,将创建一个新的套接字 (clientSocket) 用于与该客户端通信。如果连接失败,将输出错误消息并继续等待下一个连接。成功连接后,会输出一条成功连接的消息,然后可以在循环中处理客户端的请求啦!
std::cout << "Waiting for connection..." << std::endl;

sockaddr_in clientAddress{};
socklen_t clientAddressLength = sizeof(clientAddress);
int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressLength);
if (clientSocket == -1) {
std::cerr << "Failed to accept client connection." << std::endl;
continue;
}
std::cout << "Connected to client successfully!" << std::endl;

char data[23] = { "Send text from client:" };//客户端发送消息
SSIZE_T bytesSentabc = send(clientSocket, data, 22, 0);
int count = 0;
int index = 0;
const int bufferSize = 1024;
char buffer[bufferSize];
char bufferRe[bufferSize];
memset(buffer, 0, bufferSize);
memset(bufferRe, 0, bufferSize);
while (1) {
// 接收客户端发送的数据
SSIZE_T bytesRead = recv(clientSocket, bufferRe, bufferSize - 1, 0);
if (bytesRead == -1) {
std::cerr << "Failed to receive data from client." << std::endl;
// close(clientSocket);
break;
}
else if (bytesRead == 0) {
// 客户端关闭了连接
// close(clientSocket);
break;
}
else {
// 检测回车键
if (bufferRe[bytesRead - 1] == '\n' || bufferRe[bytesRead - 1] == '\r') {
index = 1;
break;
}
if (bufferRe[bytesRead - 1] != '\n' || bufferRe[bytesRead - 1] != '\r') {
buffer[count] = bufferRe[bytesRead - 1];
count++;
}
}
// 清空缓冲区
memset(bufferRe, 0, bufferSize);
if (index == 1) {//当index==1表示输入完毕
break;
}
}
/**/
char dataHash[23] = { "Send hash from client:" };//客户端发送消息摘要,用于验证消息的完整性
SSIZE_T bytesSentHash = send(clientSocket, dataHash, 22, 0);
int countHash = 0;
int indexHash = 0;
char bufferHash[bufferSize];
char bufferReHash[bufferSize];
memset(bufferHash, 0, bufferSize);
memset(bufferReHash, 0, bufferSize);
while (1) {
// 接收客户端发送的消息摘要
SSIZE_T bytesReadHash = recv(clientSocket, bufferReHash, bufferSize - 1, 0);
if (bytesReadHash == -1) {
std::cerr << "Failed to receive data from client." << std::endl;
// close(clientSocket);
break;
}
else if (bytesReadHash == 0) {
// 客户端关闭了连接
// close(clientSocket);
break;
}
else {
// 检测回车键
if (bufferReHash[bytesReadHash - 1] == '\n' || bufferReHash[bytesReadHash - 1] == '\r') {
indexHash = 1;
break;
}
if (bufferReHash[bytesReadHash - 1] != '\n' || bufferReHash[bytesReadHash - 1] != '\r') {
bufferHash[countHash] = bufferReHash[bytesReadHash - 1];
countHash++;
}
}
// 清空缓冲区
memset(bufferReHash, 0, bufferSize);
if (indexHash == 1) {//当index==1表示输入完毕
break;
}
}
// 计算哈希值
unsigned char hash[SHA256_DIGEST_LENGTH + 1]; // 添加 + 1

SHA256_CTX context;
SHA256_Init(&context);
SHA256_Update(&context, buffer, strlen(buffer));
SHA256_Final(hash, &context);
hash[SHA256_DIGEST_LENGTH] = '\0'; // 添加空字符

std::stringstream ss;
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
}
std::string hashString = ss.str();

std::cout << "Received data:" << buffer << std::endl;
std::cout << "Received hash:" << bufferHash << std::endl;
std::cout << "Expected Hash:" << hashString << std::endl;
if (bufferHash == hashString) {
std::cout << "Messages have not been tampered with and this system is safe!" << std::endl;
/*如果还想要提示发送方当前环境安全,可加可不加*/
char repeat[22] = {"Get hash from Server:"};
char repeatSafe[23] = { "So our system is safe!" };
char huiche[1] = { '\n' };
SSIZE_T bytesSenta = send(clientSocket, repeat, 21, 0);
SSIZE_T bytesSent = send(clientSocket, hashString.c_str(), hashString.length(), 0);
SSIZE_T bytesSenhui = send(clientSocket, huiche, 1, 0);
SSIZE_T bytesSentab = send(clientSocket, repeatSafe, 22, 0);
if (bytesSent == -1) {
std::cerr << "Failed to send hash to client." << std::endl;
}
}
else {
std::cout << "Messages have been tampered with and this system is insecure!" << std::endl;
/*如果还想要提示发送方当前环境不安全,可加可不加*/
char repeat[22] = {"Get hash from Server:"};
char repeatSafe[27] = { "So our system is insecure!" };
char huiche[1] = { '\n' };
SSIZE_T bytesSenta = send(clientSocket, repeat, 21, 0);
SSIZE_T bytesSent = send(clientSocket, hashString.c_str(), hashString.length(), 0);
SSIZE_T bytesSenhui = send(clientSocket, huiche, 1, 0);
SSIZE_T bytesSentab = send(clientSocket, repeatSafe, 26, 0);
if (bytesSent == -1) {
std::cerr << "Failed to send hash to client." << std::endl;
}

}
}
} // Hash()

3、run起来😉

ctrl+b,将项目编译成可执行文件,编译成功会生成一个bin目录。同样,在确保开启了tpm模拟环境(Simulator.exe)运行即可。

4、实现通讯检测

开启telnet

主要是懒得写客户端程序hh
开启虚拟机,我是开了台win7的,随后开启telnet(目的是为了实现客户端的功能)。
把勾打上即可

直接一波

把服务器端开起来(就是运行那两个exe),随后
win7连接服务器端

1
2
telnet 192.168.33.1 6666
#随后输入消息以及其sha256即可

温馨ps:https://www.strerr.com/cn/sha256.html
这里直接展示成果
若系统通讯环境安全
即两次hash结果相同,那么可以认为消息没有被篡改

若系统通讯环境不安全
即两次hash结果不相同,那么可以认为消息在传输过程中被篡改

肆、总结

1、TPM是一种硬件安全模块,可以提供多种安全功能,保护计算机系统和数据的安全性
2、其实就是将密码学的知识运用于此,只不过是基于tpm的功能去实现的。
3、TPM的意义在于:
提供可信计算基础:TPM是一种硬件安全模块,可以提供可信计算基础,确保系统中的软件和数据是可信的,防止恶意软件和攻击者对系统进行篡改和攻击。
加强系统安全性:TPM可以提供加密存储、哈希加密、数字签名等安全功能,加强系统的安全性,保护敏感信息不被未经授权的访问。
提高系统可信度:TPM可以提供远程认证功能,确保设备的身份和完整性,提高系统的可信度,防止恶意设备和攻击者对系统进行攻击和入侵。
4、遗憾|缺陷:
1)、hash时,有些小瑕疵,就是转为字符串失败了,这使我不得不使用另一种方法来实现hash转换;
2)、没有实现对称加密:具体问题就是没成功实现服务器端以及客户端共享一个密钥。
5、此实验是和lynn讨论下完成的,非常感谢。