openssl engine的实现和原理在上一篇文章:
https://blog.csdn.net/liu942947766/article/details/128837041?spm=1001.2014.3001.5502
openssl engine的加密套件协商应用
首先使用openssl engine引擎机制将算法替换为自己想要使用的第三方加密算法,然后使用openssl engine进行安全通信,不是所有的算法openssl都会支持(不支持则不会调用),可以通过查看当前版本openssl支持的加密套件有哪些,例如ECDHE-RSA-AES256-GCM-SHA384,分别对应密钥交换算法-数字签名算法-数据加密算法-摘要算法。只有openssl所支持的加密套件中的算法才支持进行安全通信,比如openssl engine利用第三方加密算法替换了openssl中rsa的算法,在ssl安全通信的时候选择加密套件协商使用ECDHE-RSA-AES256-GCM-SHA384组件后,会在ssl握手环节中使用engine中的算法进行证书验证。
如果engine替换的算法为aes,那么同理ssl握手环节中使用engine中的算法进行数据加密。
openssl engine引擎第三方算法替换原生算法是通过结构体绑定NID,例如要用引擎替换openssl协议库中的算法SHA256,那么引擎中算法结构体的NID 项应该填写“NID_sha256。
SSL_CTX_set_cipher_list 函数可以觉得选择使用的加密套件。
static const EVP_MD e_md5={
NID_sha256, //该引擎替换sha256算法
NID_md5WithRSAEncryption,
SHA_DIGEST_LENGTH,
EVP_MD_FLAG_DIGALGID_ABSENT,
md5_init,
md5_update,
md5_final,
NULL,
NULL,
SHA_CBLOCK,
sizeof(EVP_MD *) + sizeof(MD5_CTX)
};
客户端代码
/*client.c*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include "md5_engine.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include "openssl/rsa.h"
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
/*所有需要的参数信息都在此处以#define的形式提供*/
#define CERTF "../key/client.crt" /*客户端的证书(需经CA签名)*/
#define KEYF "../key/client.key" /*客户端的私钥(建议加密存储)*/
#define CACERT "../key/ca.crt" /*CA 的证书*/
#define PORT 20001 /*服务端的端口*/
#define SERVER_ADDR "0" /*服务段的IP地址*/
#define CHK_NULL(x) if ((x)==NULL) exit (-1)
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(-2); }
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(-3); }
int main ()
{
#if 1
ENGINE_load_rsapp(); //加载引擎
ENGINE *rsa_st = NULL;
rsa_st = ENGINE_by_id("emd5");//通过id返回engine对象
if(rsa_st == NULL)
{
printf("get pkcs11 engine Error\n");
return 0;
}
printf("name:%s\n",ENGINE_get_name(rsa_st));
ENGINE_register_all_digests(rsa_st);
ENGINE_set_default(rsa_st,ENGINE_METHOD_ALL);
printf("test end.\n");
#endif
int err;
int sd;
struct sockaddr_in sa;
SSL_CTX* ctx;
SSL* ssl;
X509* server_cert;
char* str;
char buf1[4096];
SSL_METHOD *meth;
int seed_int[100]; /*存放随机序列*/
/* 初始化OpenSSL */
SSL_library_init();
/*加载算法库 */
OpenSSL_add_ssl_algorithms();
/*加载错误处理信息 */
SSL_load_error_strings ();
/*采用什么协议(SSLv2/SSLv3/TLSv1)在此指定*/
meth = (SSL_METHOD *) TLSv1_2_client_method ();
/* 创建SSL会话环境 */
ctx = SSL_CTX_new (meth);
CHK_NULL(ctx);
/*验证与否,是否要验证对方*/
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
/*若验证对方,则放置CA证书*/
SSL_CTX_load_verify_locations(ctx,CACERT,NULL);
/*加载自己的证书*/
if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
exit(-2);
}
/*加载自己的私钥,以用于签名*/
if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
exit(-3);
}
/*调用了以上两个函数后,检验一下自己的证书与私钥是否配对*/
if (!SSL_CTX_check_private_key(ctx))
{
printf("Private key does not match the certificate public key\n");
exit(-4);
}
/*构建随机数生成机制,WIN32平台必需*/
srand( (unsigned)time( NULL ) );
for( int i = 0; i < 100;i++ )
seed_int[i] = rand();
RAND_seed(seed_int, sizeof(seed_int));
/* 指定加密器类型 */
SSL_CTX_set_cipher_list (ctx, "SHA256");
SSL_CTX_set_mode (ctx, SSL_MODE_AUTO_RETRY);
/*以下是正常的TCP socket建立过程 .............................. */
printf("Begin tcp socket...\n");
sd = socket (AF_INET, SOCK_STREAM, 0);
CHK_ERR(sd, "socket");
memset (&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr (SERVER_ADDR); /* Server IP */
sa.sin_port = htons (PORT); /* Server Port number */
err = connect(sd, (struct sockaddr*) &sa, sizeof(sa));
CHK_ERR(err, "connect");
/* TCP 链接已建立.开始 SSL 握手过程.......................... */
printf("Begin SSL negotiation \n");
/*申请一个SSL套接字*/
ssl = SSL_new (ctx);
CHK_NULL(ssl);
/*绑定读写套接字*/
SSL_set_fd (ssl, sd);
err = SSL_connect (ssl);
printf("链接已建立.开始 SSL 握手过程 \n");
CHK_SSL(err);
/*打印所有加密算法的信息(可选)*/
printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
/*得到服务端的证书并打印些信息(可选) */
server_cert = SSL_get_peer_certificate (ssl);
CHK_NULL(server_cert);
printf ("Server certificate:\n");
str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
CHK_NULL(str);
printf ("/t subject: %s\n", str);
free (str);
str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0);
CHK_NULL(str);
printf ("/t issuer: %s\n", str);
free (str);
X509_free (server_cert); /*如不再需要,需将证书释放 */
/* 数据交换开始,用SSL_write,SSL_read代替write,read */
printf("Begin SSL data exchange\n");
err = SSL_write (ssl, "Hello World!", strlen("Hello World!"));
CHK_SSL(err);
err = SSL_read (ssl, buf1, sizeof(buf1) - 1);
CHK_SSL(err);
buf1[err] = '\0';
printf ("Got %d chars:'%s'\n", err, buf1);
SSL_shutdown (ssl); /* send SSL/TLS close_notify */
/* 收尾工作 */
shutdown (sd,2);
SSL_free (ssl);
SSL_CTX_free (ctx);
return 0;
}
服务端代码
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "openssl/rsa.h"
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
/*所有需要的参数信息都在此处以#define的形式提供*/
#define CERTF "../key/server.crt" /*服务端的证书(需经CA签名)*/
#define KEYF "../key/server.key" /*服务端的私钥(建议加密存储)*/
#define CACERT "../key/ca.crt" /*CA 的证书*/
#define PORT 20001 /*准备绑定的端口*/
#define CHK_NULL(x) if ((x)==NULL) exit (1)
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }
int main ()
{
int err;
int listen_sd;
int sd;
struct sockaddr_in sa_serv;
struct sockaddr_in sa_cli;
int client_len;
SSL_CTX* ctx;
SSL* ssl;
X509* client_cert;
char* str;
char buf [4096];
SSL_METHOD *meth;
/* 初始化OpenSSL */
SSL_library_init();
/*加载算法库 */
OpenSSL_add_ssl_algorithms();
/*加载错误处理信息 */
SSL_load_error_strings ();
/*采用什么协议(SSLv2/SSLv3/TLSv1)在此指定*/
meth = (SSL_METHOD *) TLSv1_2_server_method ();
/* 创建SSL会话环境 */
ctx = SSL_CTX_new (meth);
CHK_NULL(ctx);
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /*验证与否*/
SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若验证,则放置CA证书*/
if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(3);
}
if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(4);
}
if (!SSL_CTX_check_private_key(ctx)) {
printf("Private key does not match the certificate public key\n");
exit(5);
}
/* 指定加密器类型 */
SSL_CTX_set_cipher_list (ctx, "SHA256");
SSL_CTX_set_mode (ctx, SSL_MODE_AUTO_RETRY);
/*开始正常的TCP socket过程.................................*/
printf("Begin TCP socket.../n");
listen_sd = socket (AF_INET, SOCK_STREAM, 0);
CHK_ERR(listen_sd, "socket");
memset (&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = inet_addr("0");
sa_serv.sin_port = htons (PORT);
int opt = 1;
if(setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
{
fprintf(stderr,"[tskIpRev]: setsockopt(SO_REUSEADDR) error\n");
return 0;
}
err = bind(listen_sd, (struct sockaddr*) &sa_serv,
sizeof (sa_serv));
CHK_ERR(err, "bind");
/*接受TCP链接*/
err = listen (listen_sd, 5);
CHK_ERR(err, "listen");
client_len = sizeof(sa_cli);
sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
CHK_ERR(sd, "accept");
/*TCP连接已建立,进行服务端的SSL过程. */
printf("Begin server side SSL\n");
ssl = SSL_new (ctx);
CHK_NULL(ssl);
SSL_set_fd (ssl, sd);
err = SSL_accept (ssl);
printf("SSL_accept finished\n");
CHK_SSL(err);
/*打印所有加密算法的信息(可选)*/
printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
/*得到服务端的证书并打印些信息(可选) */
client_cert = SSL_get_peer_certificate (ssl);
if (client_cert != NULL) {
printf ("Client certificate:\n");
str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
CHK_NULL(str);
printf ("/t subject: %s\n", str);
free (str);
str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
CHK_NULL(str);
printf ("/t issuer: %s\n", str);
free (str);
X509_free (client_cert);/*如不再需要,需将证书释放 */
}
else
printf ("Client does not have certificate.\n");
/* 数据交换开始,用SSL_write,SSL_read代替write,read */
err = SSL_read (ssl, buf, sizeof(buf) - 1);
CHK_SSL(err);
buf[err] = '\0';
printf ("Got %d chars:'%s'\n", err, buf);
err = SSL_write (ssl, "I hear you.", strlen("I hear you."));
CHK_SSL(err);
/* 收尾工作*/
shutdown (sd,2);
SSL_free (ssl);
SSL_CTX_free (ctx);
return 0;
}