OpenSSL——s_client 和 s_server

(一)生成证书的三种方式

(1)自签名根证书签发

1. 生成自签名的根证书私钥(root.key)和自签名的根证书(root.crt):

openssl req -newkey rsa:2048 -nodes -keyout root.key -x509 -days 365 -out root.crt

2. 生成服务器证书私钥(server.key)和证书签署请求(server.csr):

openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr

3. 使用自签名的根证书(root.crt)对服务器证书签名,生成服务器证书(server.crt):

openssl x509 -req -in server.csr -CA root.crt -CAkey root.key -CAcreateserial -out server.crt -days 365

4. 生成客户端证书私钥(client.key)和证书签署请求(client.csr):

openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr

5. 使用自签名的根证书(root.crt)对客户端证书签名,生成客户端证书(client.crt):

openssl x509 -req -in client.csr -CA root.crt -CAkey root.key -CAcreateserial -out client.crt -days 365

(2)自签名证书

1. 生成服务器证书和密钥:

openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt 

2. 生成客户端证书和密钥:

openssl req -newkey rsa:2048 -nodes -keyout client.key -x509 -days 365 -out client.crt 

(3)只生成服务端证书

openssl genrsa -des3 -out server.key 2048					//des3算法生成私钥
openssl rsa -text -in server.key							//查看私钥内容

openssl req -new -key server.key -out server.csr			//生成证书请求
openssl req -text -in server.csr -noout						//查看申请

openssl x509 -req -days 36500 -in server.csr -signkey server.key -out server.crt // 生成证书

(二)SSL通信过程

在使用OpenSSL进行通信时,一般主要分为以下几个步骤:
1. 协商加密参数:首先,客户端与服务器之间需要协商加密参数,例如使用的加密算法、密钥长度等。这个过程称为握手阶段。在握手阶段,客户端发送ClientHello消息,其中包含支持的加密算法列表和其他相关信息。服务器接收到ClientHello后,发送ServerHello消息,其中包含选择的加密算法和其他参数。
2. 交换证书:在握手过程中,服务器会向客户端发送证书,用于验证服务器的身份。客户端会验证证书的合法性,包括验证证书颁发者的签名和有效期等。如果验证成功,客户端会生成一个随机数,称为Pre-master secret,然后使用服务器的公钥对Pre-master secret进行加密,并发送给服务器。
3. 完成握手:服务器接收到加密后的Pre-master secret后,使用自己的私钥进行解密,得到Pre-master secret。然后,客户端和服务器会使用这个Pre-master secret来生成会话密钥,用于后续的通信加密。双方还会再次验证握手过程中的一些参数,以确保通信的安全性。
4. 加密通信:一旦握手完成,双方就可以开始安全地进行通信了。数据在传输前会使用会话密钥进行加密,以确保传输的机密性。在客户端发送数据时,会使用对称加密算法对数据进行加密,并发送给服务器。服务器收到数据时,使用相同的会话密钥进行解密,并对数据进行处理。
5. 断开连接:当通信结束时,双方可以选择断开连接。可以是客户端或服务器发送一个关闭消息给对方,以结束连接。

(三)SSL认证方式

(1)单向认证

单向认证:客户端验证服务器的合法性,服务器不需要验证客户端,因此仅需要生成服务器的证书和私钥文件即可。

(a)启动 s_server, 并使用服务器证书和私钥进行配置: 
openssl s_server -accept 443 -cert server.crt -key server.key [-tls1_2]

(b)启动 s_client, 并指定要验证的服务器证书: 
openssl s_client -connect localhost:443 -CAfile server.crt [-tls1_2]

在这里插入图片描述

(2)双向认证

双向认证:客户端验证服务器合法性、服务器也会验证客户端合法性,因此需要生成客户端和服务端的证书和私钥文件。

(a)启动 s_server, 并使用服务器证书和私钥进行配置,并指定客户端证书进行验证: 
openssl s_server -accept 443 -cert server.crt -key server.key -CAfile client.crt -Verify 1 [-tls1_2]
参数说明:
- `-accept 8443`:指定服务器监听的端口号为8443。
- `-cert server.crt`:指定服务器证书文件。
- `-key server.key`:指定服务器私钥文件。
- `-CAfile client.crt`:指定客户端证书文件,作为服务器端的根证书列表。
- `-Verify 1`:表示服务器对客户端进行证书验证。
- `[-tls1_2]`: 默认使用tlsV1.3通信, 此过程与TLSV1.2抓包不太一样, 指定TLSV1.2是 -tls1_2

(b)启动 s_client, 并使用客户端证书和私钥进行配置,并指定服务器证书进行验证: 
openssl s_client -connect localhost:443 -cert client.crt -key client.key -CAfile server.crt [-tls1_2]
参数说明:
- `-connect localhost:8443`:指定需要连接的服务器地址和端口号。
- `-cert client.crt`:指定客户端证书文件。
- `-key client.key`:指定客户端私钥文件。
- `-CAfile server.crt`:指定服务器证书文件,作为客户端的根证书列表。

在这里插入图片描述

(四)SSL demo

(1)ssl_client.c

#include <string.h>  
#include <errno.h>  
#include <sys/socket.h>  
#include <resolv.h>  
#include <stdlib.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <openssl/ssl.h>  
#include <openssl/err.h>  
  
#define MAXBUF 1024  
  
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 sockfd, len;  
    struct sockaddr_in dest;  
    char buffer[MAXBUF + 1];  
    SSL_CTX *ctx;  
    SSL *ssl;  
  
    if (argc != 3) {  
        printf("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80  \
			此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",  \
             argv[0], argv[0]);  
        exit(0);  
    }  
  
    /* SSL 库初始化 */  
    SSL_library_init();  
    OpenSSL_add_all_algorithms();  
    SSL_load_error_strings();  
    ctx = SSL_CTX_new(SSLv23_client_method());  
    if (ctx == NULL) {  
        ERR_print_errors_fp(stdout);  
        exit(1);  
    }  

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        perror("Socket");  
        exit(errno);  
    }  
    printf("socket created\n");  

    bzero(&dest, sizeof(dest));  
    dest.sin_family = AF_INET;  
    dest.sin_port = htons(atoi(argv[2]));  
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {  
        perror(argv[1]);  
        exit(errno);  
    }  
    printf("address created\n");  
  
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {  
        perror("Connect ");  
        exit(errno);  
    }  
    printf("server connected\n");  
  
    /* 基于 ctx 产生一个新的 SSL */  
    ssl = SSL_new(ctx);  
    SSL_set_fd(ssl, sockfd);  
    /* 建立 SSL 连接 */  
    if (SSL_connect(ssl) == -1)  
        ERR_print_errors_fp(stderr);  
    else {  
        printf("Connected with %s encryption\n", SSL_get_cipher(ssl));  
        ShowCerts(ssl);  
    }  
  
    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */  
    bzero(buffer, MAXBUF + 1);  
    /* 接收服务器来的消息 */  
    len = SSL_read(ssl, buffer, MAXBUF);  
    if (len > 0)  
        printf("接收消息成功:'%s',共%d个字节的数据\n",  
               buffer, len);  
    else {  
        printf  
            ("消息接收失败!错误代码是%d,错误信息是'%s'\n",  
             errno, strerror(errno));  
        goto finish;  
    }  
    bzero(buffer, MAXBUF + 1);  
    strcpy(buffer, "from client->server");  
    /* 发消息给服务器 */  
    len = SSL_write(ssl, buffer, strlen(buffer));  
    if (len < 0)  
        printf  
            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",  
             buffer, errno, strerror(errno));  
    else  
        printf("消息'%s'发送成功,共发送了%d个字节!\n",  
               buffer, len);  
  
  finish:  
    /* 关闭连接 */  
    SSL_shutdown(ssl);  
    SSL_free(ssl);  
    close(sockfd);  
    SSL_CTX_free(ctx);  
    return 0;  
}

(2)ssl_server.c

#include <stdio.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <string.h>  
#include <sys/types.h>  
#include <netinet/in.h>  
#include <sys/socket.h>  
#include <sys/wait.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <openssl/ssl.h>  
#include <openssl/err.h>  
  
#define MAXBUF 1024  

int main(int argc, char **argv)  
{  
    int sockfd, new_fd;  
    socklen_t len;  
    struct sockaddr_in my_addr, their_addr;  
    unsigned int myport, lisnum;  
    char buf[MAXBUF + 1];  
    SSL_CTX *ctx;  
  
    if (argv[1])  
        myport = atoi(argv[1]);  
    else  
        myport = 7838;  
  
    if (argv[2])  
        lisnum = atoi(argv[2]);  
    else  
        lisnum = 2;  
  
    /* SSL 库初始化 */  
    SSL_library_init();  
    /* 载入所有 SSL 算法 */  
    OpenSSL_add_all_algorithms();  
    /* 载入所有 SSL 错误消息 */  
    SSL_load_error_strings();  
    /* 以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Content Text */  
    ctx = SSL_CTX_new(SSLv23_server_method());  
    /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */  
    if (ctx == NULL) {  
        ERR_print_errors_fp(stdout);  
        exit(1);  
    }  
    /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */  
    if (SSL_CTX_use_certificate_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) {  
        ERR_print_errors_fp(stdout);  
        exit(1);  
    }  
    /* 载入用户私钥 */  
    if (SSL_CTX_use_PrivateKey_file(ctx, argv[5], SSL_FILETYPE_PEM) <= 0) {  
        ERR_print_errors_fp(stdout);  
        exit(1);  
    }  
    /* 检查用户私钥是否正确 */  
    if (!SSL_CTX_check_private_key(ctx)) {  
        ERR_print_errors_fp(stdout);  
        exit(1);  
    }  
  
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {  
        perror("socket");  
        exit(1);  
    } else  
        printf("socket created\n");  
  
    bzero(&my_addr, sizeof(my_addr));  
    my_addr.sin_family = PF_INET;  
    my_addr.sin_port = htons(myport);  
    if (argv[3])  
        my_addr.sin_addr.s_addr = inet_addr(argv[3]);  
    else  
        my_addr.sin_addr.s_addr = INADDR_ANY;  
  
    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))  
        == -1) {  
        perror("bind");  
        exit(1);  
    } else  
        printf("binded\n");  
  
    if (listen(sockfd, lisnum) == -1) {  
        perror("listen");  
        exit(1);  
    } else  
        printf("begin listen\n");  
  
    while (1) {  
        SSL *ssl;  
        len = sizeof(struct sockaddr);  
        if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr,  &len)) == -1) {  
            perror("accept");  
            exit(errno);  
        } else  
            printf("server: got connection from %s, port %d, socket %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) {  
            perror("accept");  
            close(new_fd);  
            break;  
        }  
  
        bzero(buf, MAXBUF + 1);  
        strcpy(buf, "server->client");  
        /* 发消息给客户端 */  
        len = SSL_write(ssl, buf, strlen(buf));  
  
        if (len <= 0) {  
            printf  
                ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",  
                 buf, errno, strerror(errno));  
            goto finish;  
        } else  
            printf("消息'%s'发送成功,共发送了%d个字节!\n",  
                   buf, len);  
  
        bzero(buf, MAXBUF + 1);  
        /* 接收客户端的消息 */  
        len = SSL_read(ssl, buf, MAXBUF);  
        if (len > 0)  
            printf("接收消息成功:'%s',共%d个字节的数据\n",  
                   buf, len);  
        else  
            printf  
                ("消息接收失败!错误代码是%d,错误信息是'%s'\n",  
                 errno, strerror(errno));  
        /* 处理每个新连接上的数据收发结束 */  
      finish:  
        /* 关闭 SSL 连接 */  
        SSL_shutdown(ssl);  
        /* 释放 SSL */  
        SSL_free(ssl);  
        /* 关闭 socket */  
        close(new_fd);  
    }  
  
    /* 关闭监听的 socket */  
    close(sockfd);  
    /* 释放 CTX */  
    SSL_CTX_free(ctx);  
    return 0;  
}
编译与运行:
gcc ssl-client.c -o client -lssl -lcrypto
gcc ssl-server.c -o server -lssl -lcrypto

./server 7838 1 127.0.0.1 server.crt server.key
./client 127.0.0.1 7838

(五)wireshark抓包

基于s_client 和 s_server 的单向认证和双向认证的抓包数据,分别基于TLS1.3和TLS1.2协议,已上传,参考链接:
https://download.csdn.net/download/u013213069/88300030

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸿蕊瑞琳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值