OpenSSL 中的 SSL
加密是通过 SSL/TLS
协议来实现的。SSL/TLS
是一种安全通信协议,可以保障通信双方之间的通信安全性和数据完整性。在 SSL/TLS
协议中,加密算法是其中最核心的组成部分之一,SSL可以使用各类加密算法进行密钥协商,一般来说会使用RSA
等加密算法,使用TLS
加密针对服务端来说则需要同时载入公钥与私钥文件,当传输被建立后客户端会自行下载公钥并与服务端完成握手,读者可将这个流程理解为上一章中RSA
的分发密钥环节,只是SSL
将这个过程简化了,当使用时无需关注传输密钥对的问题。
与RSA实现加密传输一致,使用SSL实现加密传输读者同样需要自行生成对应的密钥对,密钥对的生成可以使用如下命令实现;
- 生成私钥: openssl genrsa -out privkey.pem 2048
- 生成公钥: openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
执行如上两条命令,读者可得到两个文件首先生成2048
位的privkey.pem
也就是私钥,接着利用私钥文件生成cacert.pem
证书文件,该文件的有效期为1095
天也就是三年,当然此处由于是测试可以使用自定义生成,如果在实际环境中还是需要购买正规签名来使用的。
服务端实现代码与原生套接字通信保持高度一致,在连接方式上同样采用了标准API
实现,唯一的不同在于当accept
函数接收到用于请求时,我们需要通过SSL_new
产生一个SSL
对象,当需要发送数据时使用SSL_write
,而当需要接收数据时则使用SSL_read
函数,通过使用这两个函数即可保证中间的传输流程是安全的,其他流程与标准套接字编程保持一致,如下是服务端完整代码实现。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
extern "C"
{
#include
}
#pragma comment(lib, "WS2_32.lib")
#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")
#define MAXBUF 1024
int main(int argc, char** argv)
{
SOCKET sockfd, new_fd;
struct sockaddr_in socket_ptr, their_addr;
char buf[MAXBUF + 1] = {0};
SSL_CTX* ctx;
// SSL库初始化
SSL_library_init();
// 载入所有SSL算法
OpenSSL_add_all_algorithms();
// 载入所有SSL错误消息
SSL_load_error_strings();
// 以SSLV2和V3标准兼容方式产生一个SSL_CTX即SSLContentText
ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL)
{
std::cout 端口: %d --> 套接字: %d n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
}
// 基于ctx产生一个新的SSL
ssl = SSL_new(ctx);
// 将连接用户的socket加入到SSL
SSL_set_fd(ssl, new_fd);
// 建立SSL连接
if (SSL_accept(ssl) == -1)
{
closesocket(new_fd);
break;
}
// 开始处理每个新连接上的数据收发
memset(buf, 0, MAXBUF);
strcpy(buf, "[服务端消息] hello lyshark");
// 发消息给客户端
len = SSL_write(ssl, buf, strlen(buf));
if (len 0)
{
printf("[接收到客户端消息] => %s n", buf);
}
// 关闭套接字连接
finish:
SSL_shutdown(ssl);
SSL_free(ssl);
closesocket(new_fd);
}
closesocket(sockfd);
WSACleanup();
SSL_CTX_free(ctx);
system("pause");
return 0;
}
客户端实现代码同样与原生套接字编程保持一致,如下是完整代码,读者可以发现当使用connect
连接到服务端后,依然调用了SSL_connect
函数,此处的函数功能是在服务端下载证书信息,并完成证书通信验证,当验证实现后,则读者就可以向原生套接字那样去操作数据包的流向了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
extern "C"
{
#include
}
#pragma comment(lib, "WS2_32.lib")
#pragma comment(lib,"libssl.服务器托管网lib")
#pragma comment(lib,"libcrypto.lib")
#define MAXBUF 1024
void ShowCerts(SSL* ssl)
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL)
{
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("[+] 证书: %s n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("[+] 颁发者: %s n", line);
free(line);
X509_free(cert);
}
else
{
printf("[-] 无证书信息 n");
}
}
int main(int argc, char** argv)
{
int sockfd, len;
struct sockaddr_in dest;
char buffer[MAXBUF + 1] = { 0 };
SSL_CTX* ctx;
SSL* ssl;
// SSL库初始化
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
// 建立CTX上下文
ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL)
{
WSACleanup();
return 0;
}
// 创建Socket
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
return 0;
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) 0)
{
printf("接收消息: %s --> 共 %d 字节 n", buffer, len);
}
else
{
goto finish;
}
memset(buffer, 0, MAXBUF);
strcpy(buffer, "[客户端消息] hello Shark");
// 发消息给服务器
len = SSL_write(ssl, buffer, strlen(buffer));
if (len > 0)
{
printf("[+] 发送成功 n");
}
finish:
// 关闭连接
SSL_shutdown(ssl);
SSL_free(ssl);
closesocket(sockfd);
SSL_CTX_free(ctx);
system("pause");
return 0;
}
至此读者可以分别编译服务端与客户端程序,并首服务器托管网先运行服务端侦听套接字,接着运行客户端,此时即可看到如下图所示的通信流程,至此两者的通信数据包将被加密传输,从而保证了数据的安全性。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
相关推荐: MySQL 和 MariaDB 版本管理的历史背景及差异MariaDBMySQL差异关于 SQLESQLE 获取
目录 MariaDB MySQL 差异 关于 SQLE SQLE 获取 了解更多 需要说明的是 MySQL 和 MariaDB 都有社区版和企业版。对于 MySQL,这两个版本都是由同一家公司(Oracle)提供,遵循相同的版本编号体系,企业版包含更丰富的…