一、项目介绍与设计目的
研究背景:
随着信息技术的发展,数据的安全性和保密性变得越来越重要。在计算机网络通信中,数据的加密是一种常见的保护机制,它可以防止未经授权的访问者窃取、篡改、伪造或泄露数据。基于公私钥加密的方法是一种常见的加密技术,它使用一对密钥(公钥和私钥)来进行加密和解密操作。公钥用于加密数据,私钥用于解密数据,从而实现安全的数据传输和存储。
研究意义:
私人数据在网络传输和存储中面临着各种安全威胁,例如黑客攻击、数据泄露、恶意篡改等。基于公私钥加密的C/S架构数据加密上传软件的研究意义如下:
数据保密性:通过加密技术,可以保护敏感数据的保密性,确保只有授权的用户能够访问和解密数据。
数据完整性:使用数字签名技术,可以验证数据的完整性,防止数据在传输或存储过程中被篡改。
身份验证:借助公私钥加密的方法,可以实现身份验证,确保通信双方的身份真实可靠,防止冒充攻击。
安全传输:通过加密技术,可以在网络传输过程中保证数据的安全,防止数据被第三方截获和窃取。
设计目的:
基于公私钥加密的C/S架构数据加密上传软件的设计目的是为了提供一种安全可靠的数据传输解决方案。主要目的如下:
数据传输保密性:利用公私钥加密算法,对敏感数据进行安全加密,确保只有授权用户能够解密和访问数据。
数据传输完整性:通过数字签名技术,保证数据在传输过程中的完整性,防止数据被恶意篡改。
用户身份验证:使用公私钥加密进行身份验证,确保通信双方的身份真实可靠,防止冒充攻击。
用户友好性:提供简洁易用的用户界面,使用户能够轻松进行数据上传和下载操作,无需了解复杂的加密算法和协议。
通过设计和实现基于公私钥加密的C/S架构数据加密上传软件,可以为用户提供安全可靠的数据传输和存储解决方案,保障数据的机密性、完整性和可信性。
个人贡献:本项目为本人独立开发。
二、项目环境要求
操作系统:Linux操作系统
客户端开发工具:QT Creator
服务器开发工具:VS 2019
开发语言:C++语言
选择这些开发工具的理由:选择QT Creator作为客户端开发工具和Visual Studio 2019作为服务器开发工具的理由主要在于它们的跨平台性、强大的开发功能、丰富的工具和库支持以及良好的开发体验。这些工具能够帮助开发者高效地进行客户端和服务器应用程序的开发和调试,满足项目的需求并提高开发效率。
三、项目总体设计
- 需求分析:
用户需求:用户需要一个安全的数据加密上传工具,可以将敏感数据以加密形式上传到服务器。
功能需求:系统需要支持数据输入、加密、上传、用户认证等功能。
非功能需求:系统应具备高安全性、性能、可维护性和可扩展性。 - 数据库设计:
用户账户表:包括用户名、密码哈希、公钥等字段。
上传数据表:包括数据标识、加密密钥、上传时间等字段。 - 总体结构设计:
客户端-服务器架构:分为客户端和服务器两部分。
客户端负责数据输入、加密、上传。
服务器负责接收、解密、存储数据。 - 代码实现:
使用C++编程语言。
客户端实现:数据输入、加密、使用服务器公钥加密对称密钥、上传加密后的数据。
服务器实现:接收加密数据、使用私钥解密对称密钥、解密数据、存储数据。 - 用户交互:
客户端界面设计:提供用户友好的图形用户界面,包括数据输入、上传进度反馈等。
用户认证:要求用户输入用户名和密码,验证身份。 - 技术可行性:
使用Linux操作系统,QT Creator和Visual Studio 2019开发工具,C++编程语言,确保技术栈的可行性。
进行性能测试和安全性测试,确保系统能够满足需求。 - 数据加密细节:
使用公私钥加密算法,使用RSA,保护数据的机密性。
客户端生成随机对称密钥用于数据加密,然后使用服务器的公钥对其进行加密。
服务器使用私钥解密对称密钥,再用该对称密钥解密数据。
四、 项目系统的实现
客户端:
1:读取本地目录下的服务器公钥文件:
void Widget::getPubilcKey()
{
// 公钥文件路径
QString publicKeyFilePath = "./public_key_utf8.pem"; // 请将路径更改为实际的公钥文件路径
// 打开公钥文件
QFile publicKeyFile(publicKeyFilePath);
if (!publicKeyFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "无法打开公钥文件:" << publicKeyFilePath;
}
else{
cout<<"open success"<<endl;
}
// 使用 QTextStream 读取文件内容
QTextStream in(&publicKeyFile);
QString publicKeyContent = in.readAll();
// 关闭文件
publicKeyFile.close();
qDebug()<<publicKeyContent<<endl;
// 将PEM格式的公钥内容解析为RSA*对象
publicKey = loadRSAPublicKey(publicKeyContent);
if (!publicKey) {
qDebug() << "无法解析公钥";
char errorBuffer[256];
ERR_error_string(ERR_get_error(), errorBuffer);
qDebug() << "错误信息:" << errorBuffer;
}
else {
// 输出公钥内容
qDebug() << "成功加载公钥";
}
}
2:将PEM格式的公钥内容解析为RSA*对象函数
RSA* loadRSAPublicKey(const QString& publicKeyContent) {
BIO* bio = BIO_new_mem_buf(publicKeyContent.toUtf8().constData(), -1);
if (!bio) {
return nullptr;
}
RSA* publicKey = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr);
BIO_free(bio);
cout<<publicKey<<endl;
return publicKey;
}
3:加密函数:使用公钥加密数据
string Widget::encryptData(const string &data, RSA *publicKey)
{
int dataSize = RSA_size(publicKey);
int maxBlockSize = dataSize - 11; // RSA_PKCS1_PADDING
int dataLength = data.size();
int blocks = (dataLength + maxBlockSize - 1) / maxBlockSize;
std::string encryptedData;
for (int i = 0; i < blocks; i++) {
int blockSize = std::min(maxBlockSize, dataLength - i * maxBlockSize);
const char* blockData = data.c_str() + i * maxBlockSize;
unsigned char* encryptedBlock = new unsigned char[dataSize];
int encryptedLength = RSA_public_encrypt(blockSize, (const unsigned char*)blockData, encryptedBlock, publicKey, RSA_PKCS1_PADDING);
if (encryptedLength == -1) {
// 获取 OpenSSL 错误并显示错误信息
unsigned long err = ERR_get_error();
char err_msg[1024];
ERR_error_string_n(err, err_msg, sizeof(err_msg));
std::cerr << "加密失败: " << err_msg << std::endl;
return "";
}
encryptedData.append(reinterpret_cast<char*>(encryptedBlock), encryptedLength);
delete[] encryptedBlock;
}
return encryptedData;
}
服务器:
1:读取本地目录私钥函数
void Epoll::getPrivateKey()
{
// 加载私钥
FILE* privateKeyFile = fopen("./private_key_utf8.pem", "rb");
if (privateKeyFile) {
privateKey = PEM_read_RSAPrivateKey(privateKeyFile, nullptr, nullptr, nullptr);
fclose(privateKeyFile);
cout << "获取私钥成功。。。。。" << endl;
}
else {
cout << "获取私钥失败。。。。。" << endl;
}
}
2:使用私钥进行解密函数
string Epoll::decryptData(const std::string& encryptedData, RSA* privateKey) {
int dataSize = RSA_size(privateKey);
int maxBlockSize = dataSize;
int dataLength = encryptedData.size();
int blocks = (dataLength + maxBlockSize - 1) / maxBlockSize;
std::string decryptedData;
for (int i = 0; i < blocks; i++) {
int blockSize = std::min(maxBlockSize, dataLength - i * maxBlockSize);
const char* blockData = encryptedData.c_str() + i * maxBlockSize;
unsigned char* decryptedBlock = new unsigned char[dataSize];
int decryptedLength = RSA_private_decrypt(blockSize, (const unsigned char*)blockData, decryptedBlock, privateKey, RSA_PKCS1_PADDING);
if (decryptedLength == -1) {
// 获取 OpenSSL 错误并显示错误信息
unsigned long err = ERR_get_error();
char err_msg[1024];
ERR_error_string_n(err, err_msg, sizeof(err_msg));
std::cerr << "解密失败: " << err_msg << std::endl;
return "";
}
decryptedData.append(reinterpret_cast<char*>(decryptedBlock), decryptedLength);
delete[] decryptedBlock;
}
return decryptedData;
}
五、 项目性能测试与分析
1:读取公钥的时候出现读取失败的问题
解决方法:公钥读取存在识别不了公钥文件的问题,于是在生成公私钥文件时,将文件转换成以UTF-8的格式生成就解决了。
2:加密数据时候存在加密失败的问题
解决方法:由于我采用的是自定义通信协议的网络数据传输,数据量过大,所以导致加密失败,于是我在加密的时候采用了模块加密的方法,这样子就可以加密成功了。
3:有时候服务器会出现解密失败的问题
解决方法:解密失败的时候,可能存在数据网络传输的时候存在数据丢失或者数据截断的问题,这个时候我们需要发送一个反馈包给客户端,让客户端知道数据上传失败,请重新上传数据。
六、 项目功能操作实例说明
1:先运行服务器代码文件
2:再运行客户端文件
3:输入账户密码
输入错误的账户密码:
输入正确的账户密码,进入到发送数据界面:
这个过程中服务器会验证对方身份:
4:输入需要上传的数据,点击发送,客户端会解密完后打印出来客户端发送的数据:
七、数据库、项目等的用户名和密码
用户的登录账号:1001
用户的登录密码:123456
批注
需要源码的可以私信我!!!!!!!