ssl socket bio整理

目前准备自己使用基于udp的ssl流传输数据。研究了一下ssl;测试了一下demo。

复制代码
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); // #define SSLv23_client_method    TLS_client_method

 //////ssl_ctx = SSL_CTX_new(SSLv23_method());
ssl = SSL_new(ssl_ctx); SSL_set_fd(ssl, sockfd);
SSL_set_connect_state(ssl);// s->handshake_func = s->method->ssl_connect;
if (SSL_connect(ssl) == -1)
复制代码

SSL 创建:

TLS_method是怎么实现的,是通过定义了一个宏:
在methods.c文件中有如下的定义,

IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, 0, 0,
                        TLS_client_method,
                        ssl_undefined_function,
                        ossl_statem_connect, TLSv1_2_enc_data)

 

IMPLEMENT_tls_meth_func 在ssl_locl.h中

复制代码
/* Used to hold SSL/TLS functions */
struct ssl_method_st {
    int version;
    unsigned flags;
    unsigned long mask;
    int (*ssl_new) (SSL *s);
    int (*ssl_clear) (SSL *s);
    void (*ssl_free) (SSL *s);
    int (*ssl_accept) (SSL *s);
    int (*ssl_connect) (SSL *s);
    int (*ssl_read) (SSL *s, void *buf, size_t len, size_t *readbytes);
    int (*ssl_peek) (SSL *s, void *buf, size_t len, size_t *readbytes);
    int (*ssl_write) (SSL *s, const void *buf, size_t len, size_t *written);
    int (*ssl_shutdown) (SSL *s);
    int (*ssl_renegotiate) (SSL *s);
    int (*ssl_renegotiate_check) (SSL *s, int);
    int (*ssl_read_bytes) (SSL *s, int type, int *recvd_type,
                           unsigned char *buf, size_t len, int peek,
                           size_t *readbytes);
    int (*ssl_write_bytes) (SSL *s, int type, const void *buf_, size_t len,
                            size_t *written);
    int (*ssl_dispatch_alert) (SSL *s);
    long (*ssl_ctrl) (SSL *s, int cmd, long larg, void *parg);
    long (*ssl_ctx_ctrl) (SSL_CTX *ctx, int cmd, long larg, void *parg);
    const SSL_CIPHER *(*get_cipher_by_char) (const unsigned char *ptr);
    int (*put_cipher_by_char) (const SSL_CIPHER *cipher, WPACKET *pkt,
                               size_t *len);
    size_t (*ssl_pending) (const SSL *s);
    int (*num_ciphers) (void);
    const SSL_CIPHER *(*get_cipher) (unsigned ncipher);
    long (*get_timeout) (void);
    const struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */
    int (*ssl_version) (void);
    long (*ssl_callback_ctrl) (SSL *s, int cb_id, void (*fp) (void));
    long (*ssl_ctx_callback_ctrl) (SSL_CTX *s, int cb_id, void (*fp) (void));
};
复制代码

 

复制代码
# define IMPLEMENT_tls_meth_func(version, flags, mask, func_name, s_accept, \
                                 s_connect, enc_data) \
const SSL_METHOD *func_name(void)  \
        { \
        static const SSL_METHOD func_name##_data= { \
                version, \
                flags, \
                mask, \
                tls1_new, \
                tls1_clear, \
                tls1_free, \
                s_accept, \
                s_connect, \
                ssl3_read, \
                ssl3_peek, \
                ssl3_write, \
                ssl3_shutdown, \
                ssl3_renegotiate, \
                ssl3_renegotiate_check, \
                ssl3_read_bytes, \
                ssl3_write_bytes, \
                ssl3_dispatch_alert, \
                ssl3_ctrl, \
                ssl3_ctx_ctrl, \
                ssl3_get_cipher_by_char, \
                ssl3_put_cipher_by_char, \
                ssl3_pending, \
                ssl3_num_ciphers, \
                ssl3_get_cipher, \
                tls1_default_timeout, \
                &enc_data, \
                ssl_undefined_void_function, \
                ssl3_callback_ctrl, \
                ssl3_ctx_callback_ctrl, \
        }; \
        return &func_name##_data; \
        }
复制代码

宏定义替换为:

复制代码
   static const SSL_METHOD TLS_client_method_data= { \
                TLS_ANY_VERSION, \
                0, \
                0, \
                tls1_new, \
                tls1_clear, \
                tls1_free, \
                ssl_undefined_function, \
                ossl_statem_connect, \
                ssl3_read, \
                ssl3_peek, \
                ssl3_write, \
                ssl3_shutdown, \
                ssl3_renegotiate, \
                ssl3_renegotiate_check, \
                ssl3_read_bytes, \
                ssl3_write_bytes, \
                ssl3_dispatch_alert, \
                ssl3_ctrl, \
                ssl3_ctx_ctrl, \
                ssl3_get_cipher_by_char, \
                ssl3_put_cipher_by_char, \
                ssl3_pending, \
                ssl3_num_ciphers, \
                ssl3_get_cipher, \
                tls1_default_timeout, \
                &TLSv1_2_enc_data, \
                ssl_undefined_void_function, \
                ssl3_callback_ctrl, \
                ssl3_ctx_callback_ctrl, \
        }; 
复制代码

 

ssl 代码分析:

1:建立一个BIO,BIO是OpenSSL提供的用来进行算法封装的处理结构,还可以将多个算法串联起来,这样可以很方便地实现数据的封装;/

2、把套接字和BIO联系

3、 把SSL和BIO联系起来,包括读写操作

// 设置SSL读写BIO
s->rbio=rbio;
s->wbio=wbio;

 

 

 

 

复制代码
int SSL_set_fd(SSL *s, int fd)
{
    int ret = 0;
    BIO *bio = NULL;

    bio = BIO_new(BIO_s_socket());

    if (bio == NULL) {
        SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
        goto err;
    }
    BIO_set_fd(bio, fd, BIO_NOCLOSE);
    SSL_set_bio(s, bio, bio);
    ret = 1;
 err:
    return ret;
}

static const BIO_METHOD methods_sockp = {
    BIO_TYPE_SOCKET,
    "socket",
    /* TODO: Convert to new style write function */
    bwrite_conv,
    sock_write,
    /* TODO: Convert to new style read function */
    bread_conv,
    sock_read,
    sock_puts,
    NULL,                       /* sock_gets,         */
    sock_ctrl,
    sock_new,
    sock_free,
    NULL,                       /* sock_callback_ctrl */
};

const BIO_METHOD *BIO_s_socket(void)
{
    return &methods_sockp;
}

BIO *BIO_new(const BIO_METHOD *method)
{
    BIO *bio = OPENSSL_zalloc(sizeof(*bio));

    if (bio == NULL) {
        BIOerr(BIO_F_BIO_NEW, ERR_R_MALLOC_FAILURE);
        return NULL;
    }

    bio->method = method;
    bio->shutdown = 1;
    bio->references = 1;

    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data))
        goto err;

    bio->lock = CRYPTO_THREAD_lock_new();
    if (bio->lock == NULL) {
        BIOerr(BIO_F_BIO_NEW, ERR_R_MALLOC_FAILURE);
        CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
        goto err;
    }

    if (method->create != NULL && !method->create(bio)) {
        BIOerr(BIO_F_BIO_NEW, ERR_R_INIT_FAIL);
        CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
        CRYPTO_THREAD_lock_free(bio->lock);
        goto err;
    }
    if (method->create == NULL)
        bio->init = 1;

    return bio;

err:
    OPENSSL_free(bio);
    return NULL;
}
复制代码

 

复制代码
void SSL_set_accept_state(SSL *s)
{
    s->server = 1;
    s->shutdown = 0;
    ossl_statem_clear(s);
    s->handshake_func = s->method->ssl_accept;
    clear_ciphers(s);
}

int SSL_do_handshake(SSL *s)
{
    int ret = 1;

    if (s->handshake_func == NULL) {
        SSLerr(SSL_F_SSL_DO_HANDSHAKE, SSL_R_CONNECTION_TYPE_NOT_SET);
        return -1;
    }

    ossl_statem_check_finish_init(s, -1);

    s->method->ssl_renegotiate_check(s, 0);

    if (SSL_in_init(s) || SSL_in_before(s)) {
        if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) {
            struct ssl_async_args args;

            args.s = s;

            ret = ssl_start_async_job(s, &args, ssl_do_handshake_intern);
        } else {
            ret = s->handshake_func(s); // 在IMPLEMENT_tls_meth_func初始化中定义的,是ossl_statem_accept函数。
        }
    }
    return ret;
}
复制代码

 

ssl 握手为: 在IMPLEMENT_tls_meth_func初始化中定义的,是ossl_statem_accept函数。

 

 

复制代码
#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

// gcc -o sslbio sslbio.c  -O0 -ggdb  -I/usr/local/ssl/include -L/usr/local/ssl/lib -lssl -lcrypto

struct connection {
    SSL_CTX *ssl_ctx;
    SSL *ssl;
    BIO *ssl_in;
    BIO *ssl_out;
    int sockfd;
    int failed;
    int write_alerts;
} *conn;

void init_conn() {
    SSL_CTX *ctx;
    SSL *ssl;
    long options;

    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();

    ctx = SSL_CTX_new(SSLv23_client_method());
    if (!ctx) {
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    ssl = SSL_new(ctx);
    if (!ssl) {
        ERR_print_errors_fp(stderr);
        SSL_CTX_free(ctx);
        exit(EXIT_FAILURE);
    }

    conn->ssl_ctx = ctx;
    conn->ssl = ssl;

    SSL_set_app_data(conn->ssl, conn);
    SSL_set_msg_callback_arg(conn->ssl, conn);
    options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
    SSL_set_options(conn->ssl, options);

    conn->ssl_in = BIO_new(BIO_s_mem());
    conn->ssl_out = BIO_new(BIO_s_mem());
    SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
}

void send_msg(uint8_t *buf, int len) {
    send(conn->sockfd, buf, len, 0);
}

int recvbuf_msg(uint8_t *buf, int bufsize) {
    int ret = 0;
    ret = recv(conn->sockfd, buf, bufsize, 0);
    return ret;
}

int connection_handshake() {
    char tmpbuf[10240];
    char bufin[10240];
    int res = 0;
    SSL_set_connect_state(conn->ssl);
    while (1) {
        res = SSL_connect(conn->ssl);
        if (res != 1) {
            int err = SSL_get_error(conn->ssl, res);
            if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
                res = BIO_ctrl_pending(conn->ssl_out);
                if (res > 0) {
                    res = BIO_read(conn->ssl_out, tmpbuf, sizeof(tmpbuf));
                    send_msg((uint8_t *)tmpbuf, res);
                }
                res = recvbuf_msg((uint8_t *)bufin, sizeof(bufin));
                if (res > 0) {
                    BIO_write(conn->ssl_in, bufin, res);
                }
            } else {
                conn->failed++;
                fprintf(stderr, "OpenSSL: Could not generate ClientHello\n");
                conn->write_alerts++;
                return -1;
            }
        }

        if (SSL_is_init_finished(conn->ssl)) {
            printf("SSL handshake finished\n");
            return 1;
        }
    }
    return 0;
}

void send_https_get_request() {
    const char *request = "GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n";
    int request_len = strlen(request);
    char tmpbuf[10240];
    char bufin[10240];
    int res;

    // Send the HTTP GET request
    SSL_write(conn->ssl, request, request_len);

    // Read the SSL data from the ssl_out BIO and send it to the socket
    res = BIO_ctrl_pending(conn->ssl_out);
    if (res > 0) {
        res = BIO_read(conn->ssl_out, tmpbuf, sizeof(tmpbuf));
        send_msg((uint8_t *)tmpbuf, res);
    }

    // Receive the server response and feed it to ssl_in BIO
    while ((res = recvbuf_msg((uint8_t *)bufin, sizeof(bufin))) > 0) {
        BIO_write(conn->ssl_in, bufin, res);

        // Read decrypted data from SSL object
        while ((res = SSL_read(conn->ssl, tmpbuf, sizeof(tmpbuf) - 1)) > 0) {
            tmpbuf[res] = '\0';
            printf("%s", tmpbuf);
        }

        if (res < 0) {
            int err = SSL_get_error(conn->ssl, res);
            if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
                continue;
            } else {
                break;
            }
        }
    }
}

int main() {
    struct sockaddr_in server_addr;
    conn = (struct connection *)malloc(sizeof(struct connection));

    init_conn();

    conn->sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (conn->sockfd < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(443);
    inet_pton(AF_INET, "183.2.172.42", &server_addr.sin_addr); // www.baidu.com

    if (connect(conn->sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Connection failed");
        close(conn->sockfd);
        exit(EXIT_FAILURE);
    }

    if (connection_handshake() == 1) {
        printf("Handshake successful\n");
        send_https_get_request();
    } else {
        printf("Handshake failed\n");
    }

    close(conn->sockfd);
    SSL_free(conn->ssl);
    SSL_CTX_free(conn->ssl_ctx);
    free(conn);
    return 0;
}
复制代码

 

posted @   codestacklinuxer  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
历史上的今天:
2021-06-14 tcp ip 三次握手时数据结构-
点击右上角即可分享
微信分享提示