欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于openssl建立tls双向验证连接方法

程序员文章站 2022-07-12 21:54:42
...
1. 概述

本文介绍了如何基于openssl库建立tls加密通道连接。

2. 步骤
X509        *cert   = NULL;  // 客户端公钥证书指针
EVP_PKEY    *key    = NULL;  // 客户端私钥证书指针
const SSL_METHOD *meth = NULL;
g_bio_err = dup_bio_err(FORMAT_TEXT);
SSL *con = NULL;
struct sockaddr_in serv_addr;
int sockfd  = 0;
// 准备数据
// 选择客户端方法
meth = TLS_client_method();
// 初始化tls上下文
ssl_ctx = SSL_CTX_new(meth);
if (ssl_ctx == NULL) {
    LOG(ERROR,"ca_client_connect -->> create ssl_ctx failed");
    ERR_print_errors(g_bio_err);
    SSL_CTX_free(ssl_ctx);
    return -1;
}

//  加载公钥证书(用以服务端校验)
cert = load_cert("certfile.pem");
if (cert == NULL) {
    LOG(ERROR, "load_cert() failed");
    return -1;
}

// 加载私钥证书
key = load_key("keyfile.pem");
if (key == NULL) {
    LOG(ERROR, "load_key() failed");
    return -1;
}

// 将公钥证书应用到上下文
if (SSL_CTX_use_certificate(ssl_ctx, cert) <= 0) {
    LOG(ERROR, "ca_client_connect -->>SSL_CTX_use_certificate() failed");
    SSL_CTX_free(ssl_ctx);
    return -1;
}

// 将私钥应用到上下文
if (SSL_CTX_use_PrivateKey(ssl_ctx, key) <= 0) {
    LOG(ERROR, "ca_client_connect -->>SSL_CTX_use_PrivateKey() failed");
    SSL_CTX_free(ssl_ctx);
    return -1;
}

// 检验公私钥是否匹配
if (!SSL_CTX_check_private_key(ssl_ctx)) {
    LOG(ERROR, "ca_client_connect -->>SSL_CTX_use_PrivateKey() failed");
    SSL_CTX_free(ssl_ctx);
    return -1;
}

// 加载客户端ca证书 (用以校验服务端)
if (!SSL_CTX_load_verify_locations(ssl_ctx, "cafile.pem", 0))
{
    LOG(ERROR, "ca_client_connect -->>SSL_CTX_load_verify_locations() failed || ctx->cafile: %s",ctx->cafile);
    SSL_CTX_free(ssl_ctx);
    return -1;
}
// 建立连接
//建立socket连接
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
    SSL_CTX_free(ssl_ctx);
    return -1;
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ctx->host);
serv_addr.sin_port = htons(atoi(ctx->port));
ret = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (ret < 0) {
    LOG(ERROR,  "connect() failed\n");
    ERR_print_errors(g_bio_err);
    SSL_CTX_free(ssl_ctx);
    return -1;
}
// 建立tls连接
sslConn = SSL_new(ssl_ctx);
if(sslConn == NULL)
{
    SSL_CTX_free(ssl_ctx);
    return -1;
}
sbio = BIO_new_socket(sockfd, BIO_NOCLOSE);
// 设置读写io都为sslConn
SSL_set_bio(sslConn, sbio, sbio);
// 设置连接状态
SSL_set_connect_state(sslConn);
//握手
ret = SSL_do_handshake(sslConn);
if (ret != 1) {
    LOG(ERROR,  "SSL_do_handshake failed");
    ERR_print_errors(g_bio_err);
    SSL_free(sslConn);
    SSL_CTX_free(ssl_ctx);
    return -1;
}   

上述tls加密通道连接建立后得到sslConn句柄以进行读写通信。发送消息时用

// conn = sslConn, data为发送消息指针,inl为发送消息的长度
ret = SSL_write(conn, data, inl);

进行发送消息。接收消息时用SSL_read进行读取消息,如下:

// data为输出数据的指针,outl为输出数据的长度
for (;;) {
         // len = BIO_read(conn, (char *)data + offset, maxlen);
          len = SSL_read(conn, (char *)data + offset, maxlen);
         if (len <= 0) {
             // read complete
             break;
         }
         else if (len == maxlen) {
             // buffer is not enough
             return 0;
         }
         else {
            *outl += len;
            offset += len;
            maxlen -= len;
         }
    }

小结

以上即为作为客户端如何基于tls建立加密通道。