引擎库路径只有在 /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个字节的数据
请输入要发送给服务器的内容:
服务端引用