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

报错案例

Could not open file or uri for loading private key from org.openssl.engine:pkcs11:pkcs11:token=teetoken;object=teetoken;type=private;pin-value=1234
2030FEA3FFFF0000:error:16000069:STORE routines:ossl_store_get0_loader_int:unregistered scheme:../openssl-3.0.12/crypto/store/store_register.c:237:scheme=file
2030FEA3FFFF0000:error:80000002:system library:file_open:No such file or directory:../openssl-3.0.12/providers/implementations/storemgmt/file_store.c:267:calling stat()
2030FEA3FFFF0000:error:12800067:DSO support routines:dlfcn_load:could not load the shared library:../openssl-3.0.12/crypto/dso/dso_dlfcn.c:118:filename(/usr/lib/enginey
2030FEA3FFFF0000:error:12800067:DSO support routines:DSO_load:could not load the shared library:../openssl-3.0.12/crypto/dso/dso_lib.c:152:
2030FEA3FFFF0000:error:13000084:engine routines:dynamic_load:dso not found:../openssl-3.0.12/crypto/engine/eng_dyn.c:442:
2030FEA3FFFF0000:error:13000074:engine routines:ENGINE_by_id:no such engine:../openssl-3.0.12/crypto/engine/eng_list.c:433:id=pkcs11

添加如下
vim /etc/ssl/openssl.cnf

首行
openssl_conf = openssl_init

末行
[openssl_init]
engines=engine_section
 
[engine_section]
pkcs11 = pkcs11_section
 
[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so
MODULE_PATH = /usr/lib/softhsm/libsofthsm2.so
init = 0

客户端

#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;
}

交叉编译source env

INCLUDE	=
CFLAGS	= -Wall -g
LIB	=  -lssl -lcrypto -ldl -lcurses

SRCS = $(wildcard ssl_client.c)
OBJS = $(patsubst %c, %o, $(SRCS))

TARGET = client

.PHONY:all clean

all: ${TARGET}

${TARGET}: ${OBJS}
	$(CXX) -o $@ $^ ${INCLUDE} ${CFLAGS} ${LIB}
	rm -f *.o

${OBJS}: ${SRCS}
	$(CXX) -c $^ ${INCLUDE} ${CFLAGS}

clean:  
	rm -f ${TARGET} *.o

使用参数

./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博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值