欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mbed TLS 简明教程(二)

程序员文章站 2024-01-04 21:36:46
...

原文地址
译者:远方的*
转载请注明出处: http://blog.csdn.net/z2066411585

上一篇文章mbed TLS 简明教程(一) 简要描述了mbed TLS. 下面主要通过示例程序来说明mbed tls的连接过程.

示例客户端

让我们假设有一个简单的网络客户端,试图打开一个到HTTP服务器的连接并读取默认页面.应用程序如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <netdb.h>

#define SERVER_PORT 80
#define SERVER_NAME "localhost"
#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n"

int main( void )
{
    int ret, len, server_fd;
    unsigned char buf[1024];
    struct sockaddr_in server_addr;
    struct hostent *server_host;

    /*
     * Start the connection
     */
    printf( "\n  . Connecting to tcp/%s/%4d...", SERVER_NAME,
                                                 SERVER_PORT );
    fflush( stdout );

    if( ( server_host = gethostbyname( SERVER_NAME ) ) == NULL )
    {
        printf( " failed\n  ! gethostbyname failed\n\n");
        goto exit;
    }

    if( ( server_fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP) ) < 0 )
    {
        printf( " failed\n  ! socket returned %d\n\n", server_fd );
        goto exit;
    }

    memcpy( (void *) &server_addr.sin_addr,
            (void *) server_host->h_addr,
                     server_host->h_length );

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons( SERVER_PORT );

    if( ( ret = connect( server_fd, (struct sockaddr *) &server_addr,
                         sizeof( server_addr ) ) ) < 0 )
    {
        printf( " failed\n  ! connect returned %d\n\n", ret );
        goto exit;
    }

    printf( " ok\n" );

    /*
     * Write the GET request
     */
    printf( "  > Write to server:" );
    fflush( stdout );

    len = sprintf( (char *) buf, GET_REQUEST );

    while( ( ret = write( server_fd, buf, len ) ) <= 0 )
    {
        if( ret != 0 )
        {
            printf( " failed\n  ! write returned %d\n\n", ret );
            goto exit;
        }
    }

    len = ret;
    printf( " %d bytes written\n\n%s", len, (char *) buf );

    /*
     * Read the HTTP response
     */
    printf( "  < Read from server:" );
    fflush( stdout );
    do
    {
        len = sizeof( buf ) - 1;
        memset( buf, 0, sizeof( buf ) );
        ret = read( server_fd, buf, len );

        if( ret <= 0 )
        {
            printf( "failed\n  ! ssl_read returned %d\n\n", ret );
            break;
        }

        len = ret;
        printf( " %d bytes read\n\n%s", len, (char *) buf );
    }
    while( 1 );

exit:

    close( server_fd );

#ifdef WIN32
    printf( "  + Press Enter to exit this program.\n" );
    fflush( stdout ); getchar();
#endif

    return( ret );
}
  • 一个简单的客户端应用程序,只不过是:
    • 打开端口80上的连接到服务器
    • 为主页写一个标准的HTTP GET请求
    • 读取结果,直到没有更多的发送

增加安全通信

向应用程序添加SSL/TLS需要进行一些修改,主要修改是设置,配置,和拆卸SSL contexts and structures.对于连接到服务器,读取和写入数据的网络功能,这些修改是较小的.

设置

安装mbed TLS需要一个好的随机数生成器和它自己的SSL context 和SSL会话存储.对于随机数生成mbed TLS包含CTR_DRBG随机数生成器,在此也使用它.

  • mbed TLS所需的头文件:
#include "mbedtls/net.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
  • mbed TLS结构的创建和初始化如下所示:
mbedtls_net_context server_fd;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;

mbedtls_net_init( &server_fd );
mbedtls_ssl_init( &ssl );
mbedtls_ssl_config_init( &conf );
mbedtls_x509_crt_init( &cacert );
mbedtls_ctr_drbg_init( &ctr_drbg );

mbedtls_entropy_init( &entropy );
if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
                           (const unsigned char *) pers,
                           strlen( pers ) ) ) != 0 )
{
    printf( " failed\n  ! mbedtls_ctr_drbg_seed returned %d\n", ret );
    goto exit;
}

SSL 连接

在通用的TCP/IP客户端应用程序中,应用程序处理socket()和connect()调用.mbed TLS通常在网络层(net.c)内抽象出来,因此下面代码被简化了.

if( ( server_host = gethostbyname( SERVER_NAME ) ) == NULL )
    goto exit;

if( ( server_fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP) ) < 0 )
    goto exit;

memcpy( (void *) &server_addr.sin_addr, (void *) server_host->h_addr,
                 server_host->h_length );

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons( SERVER_PORT );

if( ( ret = connect( server_fd, (struct sockaddr *) &server_addr,
                     sizeof( server_addr ) ) ) < 0 )
    goto exit;

通过mbed TLS开始实际的连接如下:

if( ( ret = mbedtls_net_connect( &server_fd, SERVER_NAME,
                                     SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 )
{
    printf( " failed\n  ! mbedtls_net_connect returned %d\n\n", ret );
    goto exit;
}

SSL/TLS配置

现在,低级套接字连接已经启动并运行,我们应该配置SSL/TLS层.

首先通过设置端点和传输类型来准备SSL配置,并为安全参数加载合理的默认值.端点确定
SSL/TLS层将作为服务器(MBEDTLS_SSL_IS_SERVER)还是客户端 (MBEDTLS_SSL_IS_CLIENT).传输类型决定我们是使用 (MBEDTLS_SSL_TRANSPORT_STREAM)还是(MBEDTLS_SSL_TRANSPORT_DATAGRAM).

if( ( ret = mbedtls_ssl_config_defaults( &conf,
                MBEDTLS_SSL_IS_CLIENT,
                MBEDTLS_SSL_TRANSPORT_STREAM,
                MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 )
{
    mbedtls_printf( " failed\n  ! mbedtls_ssl_config_defaults returned %d\n\n", ret );
    goto exit;
}

身份证验证模式确定检查证书的严格程度.对于本教程,我们没有检查任何东西.
警告: 这不是你想要的完整应用程序.

mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_NONE );

这个库需要知道使用哪个随机引擎以及将哪个调试功能作为会回调.

mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg );
mbedtls_ssl_conf_dbg( &conf, my_debug, stdout );

为了使调试功能正常,我们需要在main()函数添加一个名为my_debug的调试回调函数.

static void my_debug( void *ctx, int level,
                      const char *file, int line, const char *str )
{
    ((void) level);
    fprintf( (FILE *) ctx, "%s:%04d: %s", file, line, str );
    fflush(  (FILE *) ctx  );
}

现在配置已经准备就绪,我们可以设置SSL context来使用它.

if( ( ret = mbedtls_ssl_set_hostname( &ssl, "mbed TLS Server 1" ) ) != 0 )
{
    mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hostname returned %d\n\n", ret );
    goto exit;
}

最后,SSL context需要知道它需要用来发送网络流量的输入和输出功能.

mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL );

读写数据

配置好SSL/TLS 层之后,我们应该实际写入并读取它.
用于写入网络层:

while( ( ret = write( server_fd, buf, len ) ) <= 0 )

变成

while( ( ret = mbedtls_ssl_write( &ssl, buf, len ) ) <= 0 )

从网络层读取:

ret = read( server_fd, buf, len );

变成

ret = mbedtls_ssl_read( &ssl, buf, len );

注意:如果mbedtls_ssl_read()mbedtls_ssl_write()返回一个错误,连接必须被关闭.

拆除(Teardown)

在应用程序的出口处,我们应该干净的关闭SSL/TLS连接,并且还应该销毁任何与SSL/TLS相关的信息,最后,我们释放分配的资源.

所以

close( server_fd );

变成

mbedtls_net_free( &server_fd );
mbedtls_ssl_free( &ssl );
mbedtls_ssl_config_free( &conf );
mbedtls_ctr_drbg_free( &ctr_drbg );
mbedtls_entropy_free( &entropy );

服务器认证

一个真正的应用程序应该正确认证服务器.因此,你需要一组可信的CA. 如何获取或选择取决于你的使用案例: 要连接到Web服务器,你可以使用由受信任的浏览器供应商的列表;如果你的客户端是一个只连接到你控制的一组服务器设备.你可能想成为你自己的CA等等.

mbedtls_x509_crt cacert;
const char *cafile = "/path/to/trusted-ca-list.pem";

mbedtls_x509_crt_init( &cacert );

if( ( ret = mbedtls_x509_crt_parse_file( &cacert, cafile ) ) != 0 )
{
    mbedtls_printf( " failed\n  !  mbedtls_x509_crt_parse returned -0x%x\n\n", -ret );
    goto exit;
}

mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL );

// remove the following line
// mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_NONE );

结论

在将SERVER_PORT更改为443之后,编译该应用程序并将其链接到mbed TLS库,现在我们有了一个可以将基本HTTPS发送到Web服务器的应用程序.最终的代码在库的源码中以ssl_client1.c的形式提供或参见ssl_client1.c on github.

上一篇:

下一篇: