目录结构
- ssl_client.h
- ssl_client.c
- Makefile
- ca
- ca.crt
- client.crt
- client.key
ssl_client.h
#ifndef _SSL_CLIENT_H
#define _SSL_CLIENT_H
#include <stdio.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define MAXBUF 1024
#if 0
#define PORT 6789
#define IPADDR "127.0.0.1"
#else
#define PORT 9487
#define IPADDR "10.0.0.51"
#endif
#define log(fmt,...) printf("%s:%d:"fmt"\n",__func__,__LINE__,##__VA_ARGS__)
#define BUFFER_SIZE 1024
#define HTTP_GET_KEEPALIVE "GET %s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n"
typedef struct connect_node{
int sockfd;
SSL *ssl;
}Connect_Node_T, *Connect_pNode;
SSL_CTX *glbCTX = NULL;
int connect_socket(Connect_pNode pNode);
int connect_ssl(Connect_pNode pNode);
int close_connect(Connect_pNode pNode);
int init_connect_node(Connect_pNode pNode);
int test_ssl_io(Connect_pNode pNode);
int init_ssl();
int cleanup_ssl();
int ShowCerts(SSL * ssl);
#endif
ssl_client.c
#include "ssl_client.h"
void test()
{
int ret = 0;
Connect_Node_T client_node;
ret = init_ssl();
if(ret != 0)
{
printf("init ssl error\n");
return;
}
ret = init_connect_node(&client_node);
if(ret != 0)
{
printf("init connect node error\n");
return;
}
ret = connect_socket(&client_node);
if(ret != 0)
{
printf("connect socket error\n");
return;
}
ret = connect_ssl(&client_node);
if(ret != 0)
{
printf("connect ssl error\n");
return;
}
ret = test_ssl_io(&client_node);
if(ret != 0)
{
printf("test ssl io error\n");
return;
}
close_connect(&client_node);
cleanup_ssl();
}
int main(int argc, char **argv)
{
test();
return 0;
}
int connect_socket(Connect_pNode pNode)
{
struct sockaddr_in server_addr;
if ((pNode->sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Socket");
return -1;
}
printf("socket created\n");
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
if(inet_pton(AF_INET, IPADDR, &server_addr.sin_addr) <= 0)
{
perror("inet_pton : ");
return -1;
}
printf("address created\n");
if (connect(pNode->sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
{
perror("Connect ");
return -1;
}
printf("server connected at fd :%d\n", pNode->sockfd);
return 0;
}
int close_connect(Connect_pNode pNode)
{
if(pNode->ssl != NULL)
{
SSL_shutdown(pNode->ssl);
SSL_free(pNode->ssl);
pNode->ssl = NULL;
}
if(pNode->sockfd != -1)
{
close(pNode->sockfd);
pNode->sockfd = -1;
}
return 0;
}
int init_connect_node(Connect_pNode pNode)
{
pNode->sockfd = -1;
pNode->ssl = NULL;
return 0;
}
int init_ssl()
{
char CAPATH[BUFFER_SIZE];
char *CANAME = "ca.crt";
char *clientCrtName = "client.crt";
char *clientKeyName = "client.key";
char clientCrt[BUFFER_SIZE], clientKey[BUFFER_SIZE], CAFILE[BUFFER_SIZE];
SSL_library_init();
SSL_load_error_strings();
glbCTX = SSL_CTX_new(SSLv23_client_method());
if (glbCTX == NULL)
{
perror("SSL new ctx ");
return -1;
}
memset(CAPATH, 0, sizeof(CAPATH));
if(getcwd(CAPATH, sizeof(CAPATH)) == NULL)
{
perror("getcwd");
return -1;
}
strcat(CAPATH, "/ca/");
memset(CAFILE, 0, sizeof(CAFILE));
sprintf(CAFILE, "%s%s", CAPATH, CANAME);
printf("%s\n", CAFILE);
if (SSL_CTX_load_verify_locations(glbCTX, CAFILE, NULL)<=0)
{
perror("load ca ");
return -1;
}
else
{
printf("load root crt success\n");
}
memset(clientCrt, 0, sizeof(clientCrt));
sprintf(clientCrt, "%s%s", CAPATH, clientCrtName);
if (SSL_CTX_use_certificate_file(glbCTX, clientCrt, SSL_FILETYPE_PEM) <= 0)
{
perror("load crt ");
return -1;
}
else
{
printf("load user crt successful\n");
}
memset(clientKey, 0, sizeof(clientKey));
sprintf(clientKey, "%s%s", CAPATH, clientKeyName);
if (SSL_CTX_use_PrivateKey_file(glbCTX, clientKey, SSL_FILETYPE_PEM) <= 0)
{
perror("load user key :");
return -1;
}
else
{
printf("load user key successful\n");
}
if (!SSL_CTX_check_private_key(glbCTX))
{
perror("check user key:");
return -1;
}
else
{
printf("checking user key successful\n");
}
SSL_CTX_set_verify(glbCTX, SSL_VERIFY_PEER, NULL);
return 0;
}
int cleanup_ssl()
{
if(glbCTX != NULL)
{
SSL_CTX_free(glbCTX);
}
return 0;
}
int test_ssl_io(Connect_pNode pNode)
{
int len;
char buffer[MAXBUF + 1];
bzero(buffer, MAXBUF + 1);
strcpy(buffer, "from client->server");
len = SSL_write(pNode->ssl, buffer, strlen(buffer));
if (len < 0)
{
printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",buffer, errno, strerror(errno));
}
else
{
printf("消息'%s'发送成功,共发送了%d个字节!\n",buffer, len);
}
bzero(buffer, MAXBUF + 1);
len = SSL_read(pNode->ssl, buffer, MAXBUF);
if (len > 0)
{
printf("接收消息成功:'%s',共%d个字节的数据\n", buffer, len);
}
else
{
printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));
return -1;
}
return 0;
}
int ShowCerts(SSL * ssl)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
if(SSL_get_verify_result(ssl) == X509_V_OK)
{
printf("证书验证通过\n");
}
else
{
printf("证书验证失败\n");
return -1;
}
if (cert != NULL)
{
printf("数字证书信息:\n");
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");
return -1;
}
return 0;
}
int connect_ssl(Connect_pNode pNode)
{
pNode->ssl = SSL_new(glbCTX);
if(pNode->ssl == NULL)
{
perror("new ssl ");
return -1;
}
if(SSL_set_fd(pNode->ssl, pNode->sockfd) != 1)
{
perror("ssl set fd ");
return -1;
}
if (SSL_connect(pNode->ssl) == -1)
{
perror("ssl connect ");
return -1;
}
else
{
printf("Connected with %s encryption\n", SSL_get_cipher(pNode->ssl));
}
if(ShowCerts(pNode->ssl) != 0)
{
return -1;
}
return 0;
}
makefile
EXE = ssl_client
OBJ = ssl_client.o
CC = gcc
CURRDIR = $(shell pwd)
CFLAGS = -Wall -lssl -lcrypto
INCLUDE = -I$(CURRDIR)
all : $(EXE)
$(EXE):$(OBJ)
$(CC) $(CFLAGS) $(INCLUDE) -o $(EXE) $(OBJ)
clean:
-rm -rf $(EXE) $(OBJ)
ca.crt
-----BEGIN CERTIFICATE-----
MIICZjCCAc+gAwIBAgIUFSyoO8SzaUFgjnlOZVRak6T2oqUwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTEwMjgwMzM3MjBaFw0yMjEw
MjgwMzM3MjBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwgZ8wDQYJKoZIhvcNAQEB
BQADgY0AMIGJAoGBAMV/aMOCdB2xaWC5Cr26R8rLJsK+JdC2otzs/o3qyWXggRQ6
g8vBQPFZ+OGldJtueJgrDbzrPhsJKF8eGfseXFlPg73iHkRjJ8nPe5pa3SCuaznU
3WwwzifSm4goE+x7rJyT8eTlGTdB2ONXkOR3d8kgTvLFnWPxM/6M6XUfBHszAgMB
AAGjUzBRMB0GA1UdDgQWBBTiHglejO5eKy4HlhV8bY3n3Wg1LjAfBgNVHSMEGDAW
gBTiHglejO5eKy4HlhV8bY3n3Wg1LjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4GBAF7QCa9Gb5PTYHbLTAULr1lpuuSgY89iD4l1EiAiMYq6KvbI5xmz
OoQMfcb4b286PzME71NNwNqc7i/c4iy/qZ51yDfTA7coAU+zprbp6fHUgxghmNZB
iNmqkNF0v6FYFie+BTSAU4HZbPmi6dpdg3zTVQGnv3Sx5gxjgJj7MJrK
-----END CERTIFICATE-----
client.crt
-----BEGIN CERTIFICATE-----
MIIB8TCCAVoCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAeFw0yMTEwMjkwMzAxMjVaFw0yMjEwMjkwMzAxMjVaMD0xCzAJBgNVBAYTAkNO
MQswCQYDVQQIDAJHRDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuhC7M0TCJftIVK43iUFGdsgQe
tTJjQmGcVD1/f25UPgyf2HGSMSKiy+/KdAxeAGmuDQcmbYomnOYMIycZZOo8/P+a
YetBvzdw1wTVha+Pmak7eJglKXguYng6YGLj4oHhxG3kaGd9JWejouLVR5dKTDqq
cdMUawLAzuRFD/fKkQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAKUaJDr5hkzF5isg
9/CG130K3ROQcif7+fvLu/2v36ujdbfh6hqvN2FvfofRFyK1m3rFYLTT7h2vfift
6mGg+2l+s2z1S2tYVnHNggR3WG191rXEr9nf+0wBj04S+et1YTfll2DuD2wi3YVA
G/W4AMpItfg1eGpaCeZVE4M0EE9R
-----END CERTIFICATE-----
client.key
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDuhC7M0TCJftIVK43iUFGdsgQetTJjQmGcVD1/f25UPgyf2HGS
MSKiy+/KdAxeAGmuDQcmbYomnOYMIycZZOo8/P+aYetBvzdw1wTVha+Pmak7eJgl
KXguYng6YGLj4oHhxG3kaGd9JWejouLVR5dKTDqqcdMUawLAzuRFD/fKkQIDAQAB
AoGBAIbYDglXPsSNAUJctEM9O1cW/ENMF2eMcNjLu1Toezx/M+3ulQ6cXsOA3lkr
0I4YV6bB0MgF57O6wkgcW498wPuQdI31IRKNdZcvTElOy4O+Xwv6DPJwJee8mSe+
fd+oHkVXDyvKJyHqXnzuD4Nzw3ptnmMQWDYgJoIfCzrFXYr9AkEA+jkpu9Bi7dxe
C4sfV8Y9b9rdYVKk4AYje2dngj4d6rndsTtiUZpob3Ocy29d4KJU2SK71slAzkB/
X2e8zc5pywJBAPQF1LS7YLl9XgEd4a/vArS/pCbImoAfwmffFKJBcj8u+f+yCTFx
uzeAaOVdDkKPBCi1TXk1RQcOM6lH94QtwZMCQCGYUSkdNlsXLi1AlYm0XQVKjlSF
wwss59Cmtnf/HQcpw0ELZwzrvT/Rdui9YA5L3TZ1+mBBDwliEXfetrOvFfcCQQDx
e7+2JuQeS9E2O7L23wkHg8rXUpeKiWNiVWHe+/MqUbu27SGp24nQ7/NeX4tYFJ2i
B2EmvK+VvtkjAYQROxKfAkEAlOZlJFAIRkjpROy1tfZ87aTXbShrctq4Ayt31cvE
Um5/lY1B8ZIduZM3P3X6G2cVZSPEDJ0P9R6LZkoL3kHMpg==
-----END RSA PRIVATE KEY-----
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了