著作権表示:この記事は、CSDNブロガー「liyakai_cn」の元の記事です。CC4.0BY-SAの著作権表示に従います。転載するには、元のソースリンクとこの文を添付してください。
元のリンク:https://blog.csdn.net/weixin_41564401/article/details/80232783
クライアントを再印刷して、TLSに基づいて暗号化されたチャネルを確立する方法
/*客户端代码*/
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;
}
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;
}
// conn = sslConn, data为发送消息指针,inl为发送消息的长度
ret = SSL_write(conn, data, inl);
// 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;
}
}