TLS + OpenSSL + Engine + PKCS#11 + softhsm2 安全通信

引擎库路径只有在 /lib 下才能被 "LOAD" 识别到,OpenSSL的ReadMe给的示例在/lib,大概是在构建OpenSSL时默认的configure指定了lib路径

// #define PKCS11_ENGINE_PATH "/usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so"
#define PKCS11_ENGINE_PATH "/lib/pkcs11.so"

本以为通过配置有效

./Configure --prefix=/usr/local/openssl \
            --openssldir=/usr/local/openssl

或者改Makefile有效,但是

old="ENGINESDIR=\$(libdir)\/engines-1.1"
new="ENGINESDIR=\/usr\/lib\/engine-1.1"
sed -i "s/$old/$new/g" Makefile

构建出来还是

# openssl engine pkcs11 -t
281473798283296:error:25066067:DSO support routines:dlfcn_load:could not load the shared library:crypto/dso/dso_dlfcn.c:118:filename(/home/test0923/Documents/optee_three_part/openssl/out/lib/engines-1.1/pkcs11.so): /home/test0923/Documents/optee_three_part/openssl/out/lib/engines-1.1/pkcs11.so: cannot open shared object file: No such file or directory
281473798283296:error:25070067:DSO support routines:DSO_load:could not load the shared library:crypto/dso/dso_lib.c:162:
281473798283296:error:260B6084:engine routines:dynamic_load:dso not found:crypto/engine/eng_dyn.c:434:
281473798283296:error:2606A074:engine routines:ENGINE_by_id:no such engine:crypto/engine/eng_list.c:421:id=pkcs11

但只找到了export方法

export OPENSSL_ENGINES=/usr/lib/engines-1.1

客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/err.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>

// #define PKCS11_ENGINE_PATH "/usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so"
#define PKCS11_ENGINE_PATH "/lib/pkcs11.so"
#define PKCS11_MODULE_PATH "/usr/lib/softhsm/libsofthsm2.so"

#define CERT_FILE "../hsm_pem/client.crt"
#define CA_CERT_FILE "../hsm_pem/ca.crt"

#define TOKEN_LABEL "mytoken"
#define PIN "1234"
#define KEY_ID "mytoken"

#define MAXBUF 1024

void initialize_ssl_library() {
    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();
}

void cleanup_ssl_library() {
    EVP_cleanup();
    ERR_free_strings();
}

ENGINE *load_pkcs11_engine() {
    ENGINE_load_dynamic();
    ENGINE *engine = ENGINE_by_id("dynamic");
    if (!engine) {
        fprintf(stderr, "Failed to load dynamic engine\n");
        return NULL;
    }

    if (!ENGINE_ctrl_cmd_string(engine, "SO_PATH", PKCS11_ENGINE_PATH, 0) ||
        !ENGINE_ctrl_cmd_string(engine, "ID", "pkcs11", 0) ||
        !ENGINE_ctrl_cmd_string(engine, "LIST_ADD", "1", 0) ||
        !ENGINE_ctrl_cmd_string(engine, "LOAD", NULL, 0)) {
        fprintf(stderr, "Failed to configure and load PKCS#11 engine\n");
        ENGINE_free(engine);
        return NULL;
    }

    if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", PKCS11_MODULE_PATH, 0)) {
        fprintf(stderr, "Failed to set MODULE_PATH\n");
        ENGINE_free(engine);
        return NULL;
    }

    if (!ENGINE_init(engine)) {
        fprintf(stderr, "Failed to initialize PKCS#11 engine\n");
        ENGINE_free(engine);
        return NULL;
    }

    if (!ENGINE_ctrl_cmd_string(engine, "PIN", PIN, 0)) {
        fprintf(stderr, "Failed to set PIN\n");
        ENGINE_free(engine);
        return NULL;
    }

    return engine;
}

int ShowCerts(SSL * ssl)
{
    X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl);
    // SSL_get_verify_result()是重点,SSL_CTX_set_verify()只是配置启不启用并没有执行认证,调用该函数才会真证进行证书认证
    // 如果验证不通过,那么程序抛出异常中止连接
    if(SSL_get_verify_result(ssl) == X509_V_OK){
        printf("收到server X509证书\n");
    }
	else{
		printf("未收到server X509证书\n");
		return 1;
	}
    if (cert != NULL) {
        printf("server数字证书信息:\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\n", line);
        free(line);
        X509_free(cert);
		printf("对server证书验证通过!!!\n");
    } 
	else{
		printf("无证书信息,对server证书验证失败!!!\n");
		return 1;
	}
	return 0;
}

int main(int argc, char **argv)
{
    initialize_ssl_library();

    SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
    if (!ctx) {
        fprintf(stderr, "Failed to create SSL_CTX\n");
        return 1;
    }

    // 双向验证
    // SSL_VERIFY_PEER---要求对证书进行认证,没有证书也会放行
    // SSL_VERIFY_FAIL_IF_NO_PEER_CERT---要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);

    // Load CA certificate
    if (!SSL_CTX_load_verify_locations(ctx, CA_CERT_FILE, NULL)) {
        fprintf(stderr, "Failed to load CA certificate\n");
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    // Load client certificate
    if (!SSL_CTX_use_certificate_file(ctx, CERT_FILE, SSL_FILETYPE_PEM)) {
        fprintf(stderr, "Failed to load client certificate\n");
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    // Load PKCS#11 engine
    ENGINE *engine = load_pkcs11_engine();
    if (!engine) {
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    char key_id[256];
    snprintf(key_id, sizeof(key_id), "pkcs11:token=%s;object=%s", TOKEN_LABEL, KEY_ID);
    EVP_PKEY *pkey = ENGINE_load_private_key(engine, key_id, NULL, NULL);
    if (!pkey) {
        fprintf(stderr, "Failed to load private key from PKCS#11\n");
        ENGINE_free(engine);
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
        fprintf(stderr, "Failed to use private key\n");
        EVP_PKEY_free(pkey);
        ENGINE_free(engine);
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    EVP_PKEY_free(pkey);
    ENGINE_free(engine);

    // Verify that the private key matches the certificate
    if (!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr, "Private key does not match the certificate public key\n");
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    /* 创建一个 socket 用于 tcp 通信 */
    int sockfd;
    struct sockaddr_in dest;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }
    printf("socket created success!\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 success!\n");

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected  success!\n");

    // Create SSL connection
    SSL *ssl = SSL_new(ctx);
    if (!ssl) {
        fprintf(stderr, "Failed to create SSL object\n");
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    SSL_set_fd(ssl, sockfd);

    // Perform SSL/TLS handshake with the server
    if (SSL_connect(ssl) <= 0) {
        fprintf(stderr, "SSL/TLS handshake failed\n");
        SSL_free(ssl);
        SSL_CTX_free(ctx);
        cleanup_ssl_library();
        return 1;
    }

    printf("SSL/TLS handshake successful\n");
    ShowCerts(ssl);

    char send_buffer[MAXBUF + 1];
    char recv_buffer[MAXBUF + 1];
    int len;
    while(1)
    {
        //使用SSL_write函数发送数据
        printf("请输入要发送给服务器的内容:\n");
        scanf("%s", send_buffer);
        if(!strncmp(send_buffer,"+++",3))break;     //收到+++表示退出
        /* 发消息给服务器 */
        len = SSL_write(ssl, send_buffer, strlen(send_buffer));
        if (len < 0)
            printf("消息'%s'发送失败!错误代码是%d, 错误信息是'%s'\n",send_buffer, errno, strerror(errno));
        else
            printf("消息'%s'发送成功,共发送了%d个字节! \n",send_buffer, len);
        memset(send_buffer,0,sizeof(send_buffer));
        
        
        /* 使用SSL_read函数接收数据,接收对方发过来的消息,最多接收 MAXBUF 个字节 */
        len = SSL_read(ssl, recv_buffer, MAXBUF);
        if (len > 0)
            printf("接收消息成功:'%s',共%d个字节的数据\n",recv_buffer, len);
        else 
        {
            printf("消息接收失败!错误代码是%d, 错误信息是'%s'\n",errno, strerror(errno));
            break;
        }
        memset(recv_buffer,0,sizeof(recv_buffer));
    }

    SSL_free(ssl);
    SSL_CTX_free(ctx);
    cleanup_ssl_library();
    return 0;
}

使用参数

./ssl_server 7838 1 ./pem/server.crt ./pem/server.key ./pem/ca.crt
./ssl_client 127.0.0.1 7838

测试结果,服务端

./ssl_server 7838 1 ./pem/server.crt ./pem/server.key ./pem/ca.crt
socket created success!
binded success!
begin listen,waitting for client connect...
server: got connection from 127.0.0.1, port 35546, socket 6
收到client X509证书
client数字证书信息:
证书: /CN=localhost/C=CN/ST=clientprovince/L=clientcity/O=clientorganization/OU=clientgroup
颁发者: /CN=MyCA

对client证书验证通过!!!

等待客户端发送过来的消息:
接收client消息成功:'saddfsafijd',共11个字节的数据
请输入要发送给客户端的内容:
asfdsa
消息'asfdsa'发送成功,共发送了6个字节!
等待客户端发送过来的消息:

客户端

./ssl_client 127.0.0.1 7838
socket created success!
address created success!
server connected  success!
SSL/TLS handshake successful
收到server X509证书
server数字证书信息:
证书: /CN=myserver.com
颁发者: /CN=MyCA

对server证书验证通过!!!

请输入要发送给服务器的内容:
saddfsafijd
消息'saddfsafijd'发送成功,共发送了11个字节! 
接收消息成功:'asfdsa',共6个字节的数据
请输入要发送给服务器的内容:

 服务端引用

基于openssl实现https双向身份认证及安全通信_基于openssl身份认证-CSDN博客

  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VS是微软公司开发的一款集成开发环境,而libcurl、zlib和openssl都是开源软件库。 libcurl是一个用于支持网页传输协议的客户端软件库。它支持多种传输协议,包括HTTP、FTP、SMTP等,可以方便地实现网络数据的传输和通信。libcurl提供了简单易用的API接口,具有高度的可移植性和灵活性,广泛应用于各种网络应用中。 zlib是一个用于数据压缩和解压缩的库。它可以将数据进行压缩,使其占用更少的存储空间,同时可以提高数据的传输效率。zlib可以与其他软件库配合使用,例如libcurl,以实现网络数据的传输和压缩。 openssl是一个开源的加密解密库。它提供了各种安全协议和算法的实现,包括SSL/TLS协议、RSA、AES等,用于保护网络通信安全性。openssl被广泛应用于网络安全领域,用于实现加密通信、数字证书的生成和管理等功能。 在使用方面,VS主要用于开发和编译程序,可以方便地创建和管理项目,提供了丰富的开发工具和调试功能。而libcurl、zlib和openssl是用于程序开发过程中的库文件,可以被程序调用以实现特定功能。在网络应用开发中,常常需要使用libcurl来进行网络传输,同时可以结合zlib进行数据的压缩和解压缩,而openssl可以提供数据的安全加密和解密功能。 总之,VS是一个开发环境,而libcurl、zlib和openssl是开发中常用的软件库,它们在不同领域发挥着重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值