wolfssl移植替换openssl

本文的核心是讲怎么把项目中的openssl库替换成wolfssl库,并且尽可能的少改工程代码。

一、为什么选择wolfssl替换已有的openssl库

  1. wolfssl比较小。
    要从工程中移除openssl的最主要的一个原因是openssl太大了。在嵌入式工程中,为了实现一个小的功能,引用了一个openssl,编译生成的固件太大了,flash装不下了。而wolfssl在满足基本需求的同时,又能大大减小固件的大小。
  2. wolfssl性能相对优秀。
    和openssl对比,wolfssl的性能更加优秀,关于openssl和wolfssl的对比,可从wolfssl官网得到详尽的答案:openssl和wolfssl的特点对比
  3. wolfssl方便移植。
    wolfssl 除了本地API之外,wolfssl还提供了一个openssl兼容性头文件wolfssl/openssl/ssl.h ,以简化转换为使用wolfssl或帮助将现有的openssl应用程序移植到wolfssl。openssl兼容层将最常用的openssl命令的子集映射到wolfssl的本地API函数。这应该允许在应用程序或项目中通过wolfssl轻松替换openssl,而无需更改大量代码。

二、如何编译安装wolfssl

首先需要去官网下载安装包:wolfssl下载
或者去github上下载:wolfssl下载
学习资料和论坛都在官网上可以找到,这是wolfssl的使用手册
在PC linux系统编译:

  1. ./configure
  2. make
  3. sudo make install

./configure后面可以跟很多选项,比如指明编译链、安装路径、静态库或者动态库等。具体说明可以执行 ./configure –help
官方关于configure的说明参考:configure –help 说明链接

交叉编译链编译:
需要用–host选项来指明交叉编译链。

./configure --prefix=$PWD/output --host=arm-linux --enable-openssh --enable-static --disable-shared

需要注意的是交叉编译链路径需要加入到PATH环境变量中,不然也是找不到的。
执行configure的目的是为了生成Makefile。需要确保在当前路径有output目录,这是通过–prefix决定的,不指定将默认安装在/usr/local目录下。生成的libwolfssl.a 静态库放到工程代码中,然后修改相关Makefile将库包含进来。并且在源码中需要包含头文件

三、移植过程遇到的问题

出现如下错误信息,找不到库:

./echoserver: error while loading shared libraries: libwolfssl.so.16: cannot open shared object file: No such file or directory

有以下几种解决方法:

  1. 第一种方法 最佳解决方法:
    因为编译开源库是默认配置安装的,所以库的路径在/usr/local/lib/下,需要软链接到系统能找到库的地方,也就是系统默认库路径下
    sudo ln -s /usr/local/lib/libwolfssl.so.16.0.0 /usr/lib/libwolfssl.so.16
  2. 第二种方法
    在configure阶段用–prefix参数把安装目录指定到/usr目录下,会在/usr/include和/usr/local/lib/下生成头文件和库
    例如:./configure –prefix=/usr
  3. 第三种方法
    针对没有root权限的情况,大多数情况都是执行:
    export LD_LIBRARY_PATH=/yourlocalpath:$LD_LIBRARY_PATH
  4. 第四种方法
    可以直接加上路径名或者以 -L,-l 指定库

gcc编译时库的依赖顺序经常出现问题:

静态库经常会出现依赖顺序问题,动态库则不会。
当A库依赖于B库时(A库引用B库的函数时,应该写在前面)则在编译的时候链接顺序应该是 -lA -lB 。

虽然提供了兼容层,但是wolfssl的机制和openssl的机制还是有些不同。尝试移植wolfssl库到嵌入式板子。移植后,写的demo程序在板子上可以运行,嵌入到工程中也没有问题。但是,移植到“发送邮件“ 那部分代码,ssl_connect函数连接远程服务器不成功。发现wolfssl和openssl在证书验证机制上是有区别的。wolfssl 客户端默认需要加载证书,而openssl则不用。这意味着如果没有加载CA来验证服务器,则会出现连接错误。官网说可以调用SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0) 来模拟openssl的行为,但这将降低安全性。经过反复试验,还是连接不上。 可能客户端还是需要加载证书。看了一些wolfssl例子,客户端都是需要加载数字证书的。如果是这样的情形,那对应每种类型的邮箱所要加载的证书也不一样(如qq和163),用户绑定的邮箱种类也不可能全部一样,处理起来将会变得很麻烦。 由于目前移植存在风险,所以还是用原来的openssl库。 这是目前遗留的问题,后续再看看。

四、服务器代码

用简单的例子来模拟,可用抓包软件研究一下。
OPEN_SSL宏来控制是否要加入wolfssl机制。客户端和服务器都是不需要加载证书的,因为客户端使用了SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0) 函数。

//server_echo.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>

#include <signal.h>   //信号
#include <wolfssl/openssl/ssl.h>  //wolfssl转openssl的兼容层

#define SERV_PORT        9877   
#define LISTENQ     1024
#define MAXLINE     4096

#define OPEN_SSL

static int cleanup;     /* To handle shutdown */
void sig_handler(const int sig)
{
    printf("\nSIGINT handled.\n");
    cleanup = 1;
    return;
}


#ifdef OPEN_SSL
void str_echo_ssl(WOLFSSL* ssl)
{
    int         n;
    char        buf[MAXLINE];
    while ( (n = SSL_read(ssl, buf, MAXLINE)) > 0) {
        if(SSL_write(ssl, buf, n) != n) {
            printf("wolfSSL_write failed");
        }
    }
    if( n < 0 )
        printf("wolfSSL_read error = %d\n", wolfSSL_get_error(ssl,n));

    else if( n == 0 )
        printf("The peer has closed the connection.\n");
}
#else
void str_echo(int sockfd)
{
    char buff[MAXLINE];
    int length=0;
    printf("server begin recv\n");
    while(length=recv(sockfd,buff,MAXLINE,0)) //这里是分包接收,每次接收4096个字节
    {
        if(length<0)
        {
            perror("recv");
            exit(-1);
        }
        printf("server send\n");
        if (send(sockfd,buff,MAXLINE,0) < 0)
        {
            perror("Send");
            exit(-1);
        }
        bzero(buff, sizeof(buff));
    }
}
#endif
int main(int argc, char **argv)
{
    int                 listenfd, connfd,fpid;
    pid_t               childpid;
    socklen_t           clilen;
    struct sockaddr_in  cliaddr, servaddr;
    struct sigaction    act, oact;      /* structures for signal handling */    
    act.sa_handler = sig_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT, &act, &oact);

#ifdef OPEN_SSL
    wolfSSL_Init();      /* Initialize wolfSSL */
    WOLFSSL_CTX* ctx;

    /* Create and initialize WOLFSSL_CTX structure */
    if ( (ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())) == NULL){
        fprintf(stderr, "wolfSSL_CTX_new error.\n");
        exit(EXIT_FAILURE);
    }

    /* Load CA certificates into WOLFSSL_CTX */
    /*if (wolfSSL_CTX_load_verify_locations(ctx,"../certs/ca-cert.pem",0) !=
            SSL_SUCCESS) {
        fprintf(stderr, "Error loading ../certs/ca-cert.pem, "
                "please check the file.\n");
        exit(EXIT_FAILURE);
    }*/

    /* Load server certificate into WOLFSSL_CTX */
    if (wolfSSL_CTX_use_certificate_file(ctx,"../certs/server-cert.pem",
                SSL_FILETYPE_PEM) != SSL_SUCCESS) {
       fprintf(stderr, "Error loading ../certs/server-cert.pem, "
               "please check the file.\n");
       exit(EXIT_FAILURE);
    }

    /* Load server key into WOLFSSL_CTX */
    if (wolfSSL_CTX_use_PrivateKey_file(ctx,"../certs/server-key.pem",
                SSL_FILETYPE_PEM) != SSL_SUCCESS) {
       fprintf(stderr, "Error loading ../certs/server-key.pem, "
               "please check the file.\n");
       exit(EXIT_FAILURE);
    }   
#endif

    //建立socket连接
    if ((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    printf("create socket success!\n");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(SERV_PORT);

    // 设置套接字选项避免地址使用错误,为了允许地址重用,我设置整型参数(on)为 1 (不然,可以设为 0 来禁止地址重用)
    int on=1;  
    if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)  
    {  
        perror("setsockopt failed");  
        exit(-1);  
    }

    if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
        perror("bind");
        exit(-1);
    }
    printf("Bind success!\n");
    if(listen(listenfd, LISTENQ) == -1)
    {
        perror("listen");
        exit(-1);
    }

    while(cleanup != 1)
    {
        clilen = sizeof(cliaddr);
        printf("begin accept!\n");
        if ((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0)
        {
            perror("accept");
            exit(-1);           
        }
        //////////
#ifdef OPEN_SSL
        WOLFSSL* ssl;
        /* Create WOLFSSL Object */
        if( (ssl = wolfSSL_new(ctx)) == NULL) {
           printf("wolfSSL_new error.\n");
           exit(-1);
        }

        wolfSSL_set_fd(ssl, connfd);
        str_echo_ssl(ssl);              /* process the request */
        wolfSSL_free(ssl);          /* Free WOLFSSL object */
#else
        /////////
        printf("begin fork!\n");
        fpid=fork();   
        if (fpid < 0)   
        {
            perror("fork");
            exit(-1);
        } 
        else if (fpid == 0) //child process
        {  
            close(listenfd);    // close listening socket
            str_echo(connfd);   // process the request
            exit(0);
        }
#endif
        close(connfd);          // parent closes connected socket
    }
#ifdef OPEN_SSL
    wolfSSL_CTX_free(ctx);          /* Free WOLFSSL_CTX */
    printf("wolfSSL_CTX freed\n");
    wolfSSL_Cleanup();              /* Free wolfSSL */
    printf("wolfSSL freed\n");
#endif
    exit(1);
}

五、客户端代码

OPEN_SSL宏来控制是否要加入wolfssl机制。

//client_echo.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <wolfssl/openssl/ssl.h>  //wolfssl转openssl的兼容层

#define MAXLINE     4096
#define SERV_PORT        9877   

#define OPEN_SSL

#ifdef OPEN_SSL
void str_cli_ssl(FILE *fp, SSL* ssl)
{
    char    sendline[MAXLINE], recvline[MAXLINE];
    int     n = 0;

    while (fgets(sendline, MAXLINE, fp) != NULL) {

        if(SSL_write(ssl, sendline, strlen(sendline)) !=
                strlen(sendline)){
            printf("wolfSSL_write failed");
        }

        if ((n = SSL_read(ssl, recvline, MAXLINE)) <= 0)
            printf("wolfSSL_read error");

        recvline[n] = '\0';
        fputs(recvline, stdout);
    }
}
#else
void str_cli(FILE *fp, int sockfd)
{
    char    sendline[MAXLINE], recvline[MAXLINE];

    while(fgets(sendline, MAXLINE, fp) != NULL)
    {
        printf("sendline : %s\n",sendline);
        if (send(sockfd,sendline,strlen(sendline),0) < 0)
        {
            perror("Send");
            exit(-1);
        }
        if (recv(sockfd,recvline,MAXLINE,0) < 0 )
        {
            perror("recv");
            exit(-1);
        }
        printf("recvline : %s\n",recvline);
        fputs(recvline, stdout);
    }
}
#endif

int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_in  servaddr;

    if (argc != 2)
    {
        perror("usage: tcpcli <IPaddress>");
        exit(-1);
    }


    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("sockfd");
        exit(-1);
    }
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)
    {
        perror("inet_pton");    
        exit(-1);       
    }

    if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
        perror("connect");
        exit(-1);       
    }

/////////
#ifdef OPEN_SSL
    SSL_CTX* ctx;
    SSL_library_init();
    SSL_load_error_strings();
    ctx = SSL_CTX_new (SSLv23_client_method());
    if((ctx) == NULL)
    {
        printf("Fun:%s\tSSL_CTX ERROR\n", __FUNCTION__);
        return -1;
    }
    printf("tim add SSL_CTX_set_verify test############\n");
    SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);//fix SSL_connect fail
    SSL *ssl;

    if( (ssl = SSL_new(ctx)) == NULL) {
        fprintf(stderr, "wolfSSL_new error.\n");
        exit(EXIT_FAILURE);
    }

    SSL_set_fd(ssl, sockfd);
    int ssl_ret;
    int fgCycleFlag = 1;
    while(fgCycleFlag )
    {
        ssl_ret = SSL_connect(ssl);
        switch(SSL_get_error(ssl, ssl_ret))//这里出错
        {
            case SSL_ERROR_NONE:
                printf("Fun:%s\tSSL_ERROR_NONE,ssl_ret = %d\n", __FUNCTION__,ssl_ret);
                fgCycleFlag = 0;
                usleep(100000);
                break;
            case SSL_ERROR_WANT_WRITE:
                printf("Fun:%s\tSSL_ERROR_WANT_WRITE,ssl_ret = %d\n", __FUNCTION__,ssl_ret);
                usleep(100000);
                break;
            case SSL_ERROR_WANT_READ:
                printf("Fun:%s\tSSL_ERROR_WANT_READ,ssl_ret = %d\n", __FUNCTION__,ssl_ret);
                usleep(100000);
                break;
            default:    
                printf("SSL_connect:%s\n", __FUNCTION__);
                return -1;
        }   
    }
    str_cli_ssl(stdin, ssl);        /* do it all */
//程序不会走这里,缺少回收资源的机制,实际运用中需要及时对资源进行释放!!
    if(ssl != NULL)
    {
        printf("Fun:%s Close SSL\n", __FUNCTION__);
        SSL_shutdown (ssl); 
        SSL_free (ssl);    /* Free SSL object */
        ssl = NULL;
    }
    if(ctx != NULL)
    {
        printf("Fun:%s Close SSL\n", __FUNCTION__);
        SSL_CTX_free (ctx); 
        ctx = NULL;
    }
#else
/////////
    str_cli(stdin, sockfd);
#endif
    exit(1);
}

六、编译结果

gcc server_ssl_echo.c -o server_ssl_echo -lwolfssl
gcc client_ssl_echo.c -o client_ssl_echo -lwolfssl

ubuntu@ubuntu:~/csdn/echo/server_ssl$ ./server_ssl_echo                                 
create socket success!
Bind success!
begin accept!
ubuntu@ubuntu:~/csdn/echo/client_ssl$ ./client_ssl_echo 127.0.0.1                       
tim add SSL_CTX_set_verify test############
Fun:main        SSL_ERROR_NONE,ssl_ret = 1
456
456

猜你喜欢

转载自blog.csdn.net/u014530704/article/details/79919876