Linux使用openssl对socket加密【2】

上一篇博客介绍了使用openssl对socket加密使用到的库函数,也解决了上一篇博客中说到的问题,首先就是要生成openssl证书,大家可以参考本篇博客(https://blog.csdn.net/fly2010love/article/details/46415307),就不多说了。
还有一个问题就是编译的问题,刚开始我以为是库没安装好,其实是没有生成证书,而且使用gcc编译的时候要使用-L参数链接到库函数所在的路径,刚开始我没加-L会出现以下情况:
Linux使用openssl对socket加密【2】_第1张图片
这是因为引用openssl静态库libcrypto.a、libssl.a和动态库.so路径出现问题,如果你不知道它的路径,可以学习Linux下grep和find命令,我使用find / -name libssl.*命令寻找.so和.a的路径,如下:在这里插入图片描述
然后编译的时候加-L跟上路径就可以了。

客户端

解决好了问题,再来看一下代码,首先来说一下客户端编写思路:
(1)SSL库初始化;
(2)载入所有SSL算法;
(3)载入所有SSL错误信息;
(4)建立会话环境CTX;
(5)创建socket套接字;
(6)TCP连接connect(sockfd);
(7)申请一个SSL套接字;
(8)将socket套接字加入到SSL;
(9)进行SSL握手;
(10)与服务器进行数据收发;
(11)关闭SSL套接字;
(12)释放SSL套接字;
(13)关闭socket套接字;
(14)释放会话环境;
以下为流程图:
Linux使用openssl对socket加密【2】_第2张图片
下面是代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include "init_socket.c"

#define MAXSIZE 1024

#define CA_CERT_FILE "ca.crt"
#define CLIENT_CERT_FILE "client.crt"
#define CLIENT_KEY_FILE "client.key"
int g_stop=0;
void sig_handler(int signum)/*signal信号处理函数*/
{
        if( SIGUSR1==signum )
        {
                g_stop=-1;
        }
}

/*打印证书信息*/
void ShowCerts(SSL * ssl)
{
        X509 *cert;
        char *line;

        cert = SSL_get_peer_certificate(ssl);
        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");
}

int main(int argc,char **argv)
{
        int                      ch;
        int                      len;
        int                      rv;
        int                      sockfd = -1;
        int                      server_port = 0;

        char                     send_buf[MAXSIZE];
        char                     rev_buf[MAXSIZE];
        char                     *server_ip = NULL;



        SSL_CTX                  *ctx;
        SSL                      *ssl;


        struct option opt[] = {
                {"ipaddr", required_argument, NULL, 'i'},
                {"port", required_argument, NULL, 'p'},
                {"help", no_argument, NULL, 'h'},
                {NULL, 0, NULL, 0}
        };
while ( (ch = getopt_long(argc, argv, "i:p:h", opt, NULL))!=-1 )
        {
                switch(ch)
                {
                        case 'i':
                                server_ip = optarg;
                                break;
                        case 'p':
                                server_port = atoi(optarg);
                                break;
                        case 'h':
                                print_help(argv[0]);
                                return 0;
                }
        }
        if (!server_ip)
        {
                print_help(argv[0]);
                return 0;
        }

        /*SSL库初始化*/
        SSL_library_init();

        /*加载所有算法*/
        OpenSSL_add_all_algorithms();
        /*载入所有错误信息*/
        SSL_load_error_strings();
        ERR_load_BIO_strings();

        ctx = SSL_CTX_new(SSLv23_client_method());
        if (NULL == ctx)
        {
                printf("SSL_CTX_new error!\n");
                ERR_print_errors_fp(stdout);
                exit(1);
        }

        // 要求校验对方证书,表示需要验证服务器端,若不需要验证则使用  SSL_VERIFY_NONE
        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

        // 加载CA的证书
        printf("SSL_CTX_load_verify_locations start!\n");
        if(!SSL_CTX_load_verify_locations(ctx, CA_CERT_FILE, NULL))
        {
                printf("SSL_CTX_load_verify_locations error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }

        // 加载自己的证书 
        if(SSL_CTX_use_certificate_file(ctx, CLIENT_CERT_FILE, SSL_FILETYPE_PEM) <= 0)
        {
                printf("SSL_CTX_use_certificate_file error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }

        if(SSL_CTX_use_PrivateKey_file(ctx, CLIENT_KEY_FILE, SSL_FILETYPE_PEM) <= 0)
        {
                printf("SSL_CTX_use_PrivateKey_file error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }
        if(!SSL_CTX_check_private_key(ctx))
        {
                printf("SSL_CTX_check_private_key error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }

        /*socket初始化*/
        sockfd=client_init(server_ip,server_port);

        /*创建SSL套接字*/
        ssl = SSL_new(ctx);
        if (NULL==ssl)
        {
                printf("SSL_new error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }

        SSL_set_fd(ssl, sockfd);

        if (SSL_connect(ssl) == -1)
        {
                printf("SSL_new error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }
        else
        {
                printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
                ShowCerts(ssl);
        }

        signal(SIGUSR1,sig_handler);
        while (!g_stop)
        {
                /*进行数据传送*/
                printf("Input message:\n");
                bzero(send_buf, MAXSIZE + 1);
                fgets(send_buf,MAXSIZE,stdin);

                len = SSL_write(ssl, send_buf, strlen(send_buf));
                if (len<0)
                {
                        printf("消息'%s'发送失败!错误信息是'%s'\n", send_buf, strerror(errno));
                        goto finish;
                }
                else
                {
                        printf("消息%s发送成功,共发送了%d个字节!\n", send_buf, len-1);
                }


                memset(rev_buf, 0, sizeof(rev_buf));
                rv = SSL_read(ssl, rev_buf, sizeof(rev_buf));
                if (rv<0)
                {
                        printf("read data from server failure:%s\n",strerror(errno));
                        goto finish;
                }
                else
                {
                        printf("read %zd bytes data from server is:%s\n",strlen(rev_buf)-1,rev_buf);
                }
        }
finish:
        SSL_shutdown(ssl);
        SSL_free(ssl);
        close(sockfd);
        SSL_CTX_free(ctx);
        return 0;
}

服务器端

服务器端的编程思路为:
(1)SSL库初始化;
(2)载入所有SSL算法;
(3)载入所有SSL错误信息;
(4)建立会话环境CTX;
(5)载入用户证书;
(6)载入用户私钥;
(7)检查用户私钥是否正确;
(8)服务器端socket初始化;
(9)TCP接收客户端请求;
(10)申请一个SSL套接字;
(11)将socket套接字加入到SSL;
(12)进行SSL握手;
(13)与客户端进行数据传送;
(14)关闭SSL套接字;
(15)释放SSL套接字;
(16)关闭socket套接字;
(17)释放会话环境;
流程图:
Linux使用openssl对socket加密【2】_第3张图片
以下为代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include "init_socket.c"

#define MAXSIZE 1024

#define CA_CERT_FILE "ca.crt"
#define SERVER_CERT_FILE "server.crt"
#define SERVER_KEY_FILE "server.key"

int g_stop=0;
void sig_handler(int signum)
{
        if( SIGUSR1==signum )
        {
                g_stop=-1;
        }
}
void print_help(char *progname)
{
        printf("The progname is:%s\n",progname);
        printf("-p(--port):specify listen port!\n");
        printf("-h(--help):print help information!\n");
        return ;
}

int main(int argc, char **argv)
{
        int                   ch;
        int                   port = 0;
        int                   len = 0;
        int                   rv = 0;
        int                   clifd = -1;
        int                   sock_fd = -1;

        char                  send_buf[MAXSIZE];
        char                  rev_buf[MAXSIZE];

        SSL_CTX               *ctx;
        SSL                   *ssl;

        struct option opt[] = {
                {"port", required_argument, NULL, 'p'},
                {"help", no_argument, NULL, 'h'},
                {NULL, 0, NULL, 0}
        };
        while ( (ch = getopt_long(argc, argv, "p:h", opt, NULL))!=-1 )
        {
                switch(ch)
                {
                        case 'p':
                                port=atoi(optarg);
                                break;
                        case 'h':
                                print_help(argv[0]);
                                return 0;
                }
        }

        if (!port)
        {
                print_help(argv[0]);
                return 0;
        }

        /*错误队列*/
        ERR_load_BIO_strings();

        /*SSL初始化*/
        SSL_library_init();
        printf("SSL_library_init ok!\n");

        OpenSSL_add_all_algorithms();
        SSL_load_error_strings();
        ERR_load_BIO_strings();

        /*建立会话环境*/
        printf("建立会话环境....\n");

        ctx = SSL_CTX_new(SSLv23_server_method());
        if (ctx == NULL)
        {
                printf("建立会话环境失败!\n");
                ERR_print_errors_fp(stdout);
                exit(1);
        }

        // 是否要求校验对方证书 此处不验证客户端身份所以为: SSL_VERIFY_NONE
        SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);

        // 加载CA的证书  
        if(!SSL_CTX_load_verify_locations(ctx, CA_CERT_FILE, NULL))
        {
                printf("SSL_CTX_load_verify_locations error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }

        // 加载自己的证书  
        if(SSL_CTX_use_certificate_file(ctx, SERVER_CERT_FILE, SSL_FILETYPE_PEM) <= 0)
        {
                printf("SSL_CTX_use_certificate_file error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }

        /* 加载自己的私钥  私钥的作用是,ssl握手过程中,对客户端发送过来的随机
         * 消息进行加密,然后客户端再使用服务器的公钥进行解密,若解密后的原始消息跟
         * 客户端发送的消息一直,则认为此服务器是客户端想要链接的服务器*/
        if(SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY_FILE, SSL_FILETYPE_PEM) <= 0)
        {
                printf("SSL_CTX_use_PrivateKey_file error!\n");
                ERR_print_errors_fp(stderr);
                return -1;
        }
        /*检查用户私钥是否正确*/
        if (!SSL_CTX_check_private_key(ctx))
        {
                printf("SSL_CTX_check_private_key error!\n");
                ERR_print_errors_fp(stdout);
                return -1;
        }

        /*socket初始化*/
        sock_fd = server_init(NULL, port);
        if (sock_fd<0)
        {
                printf("socket_init failure!\n");
                return -1;
        }

        printf("socket_init ok!\n");

        clifd = accept(sock_fd, (struct sockaddr *)NULL, NULL);
        if (clifd<0)
        {
                printf("accept new client failure:%s\n",strerror(errno));
                return -1;
        }
        printf("Accept new client[%d] successfully!\n",clifd);

        /*申请SSL套接字*/
        ssl = SSL_new(ctx);
        SSL_set_fd(ssl, clifd);

        if (SSL_accept(ssl) == -1)
        {
                printf("SSL_accept failure:%s!\n",strerror(errno));
                close(clifd);
                return -2;
        }
        signal(SIGUSR1,sig_handler);

        while (!g_stop)
        {
                //printf("start to accept new client incoming...\n");

                memset(rev_buf, 0,sizeof(rev_buf));
                len = SSL_read(ssl,rev_buf, MAXSIZE);
                if (len > 0)
                {
                        printf("接收消息成功:%s,共%d个字节的数据\n",rev_buf, len-1);
                }
                else
                {
                        printf("消息接收失败!错误信息是'%s'\n", strerror(errno));
                }
                memset(send_buf,0,sizeof(send_buf));
                printf("Input reply message:\n");
                fgets(send_buf,MAXSIZE,stdin);

                rv = SSL_write(ssl, send_buf, MAXSIZE);
                if (rv>0)
                {
                        printf("send %ld bytes data to client:%s\n", strlen(send_buf)-1, send_buf);
                }
                else
                {
                        printf("send data to client failure:%s\n", strerror(errno));
                        break;
                }
        }
        SSL_shutdown(ssl);
        SSL_free(ssl);
        close(clifd);
        close(sock_fd);
        SSL_CTX_free(ctx);
        return 0;
}

可以自己编写makefile,以下为运行结果:
客户端:Linux使用openssl对socket加密【2】_第4张图片
服务器端:Linux使用openssl对socket加密【2】_第5张图片

代码调试

调试代码的时候可以学习一下openssl的命令,输入openssl --help可以找到s_client和s_server两个命令,具体的用法可以自行百度,这里给出命令格式:
测试客户端代码,就用服务器端命令:openssl s_server -cert server.crt -key server.key -CAfile ca.crt -port 8888
参数:-cert服务器端的证书,-key服务器端的私钥,-CAfile生成的CA证书,-port需要监听的端口。
测试服务端器代码,就用客户端命令:openssl s_client -connect ip:port -cert client.crt -key client.key -CAfile ca.crt
参数:-connect后面跟要连接的客户ip和端口,其它换成客户端的证书和私钥即可。

参考:
1、https://blog.csdn.net/xs574924427/article/details/17240793
2、https://blog.csdn.net/fly2010love/article/details/46458963
3、https://blog.csdn.net/fly2010love/article/details/46458805
4、https://blog.csdn.net/fly2010love/article/details/46415307

你可能感兴趣的:(项目)