前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mbed TLS 简明教程(二)

mbed TLS 简明教程(二)

作者头像
程序手艺人
发布2018-05-18 15:28:47
6.5K0
发布2018-05-18 15:28:47
举报
文章被收录于专栏:程序手艺人程序手艺人

译者:远方的自由 转载请注明出处: http://blog.csdn.net/z2066411585

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

示例客户端 

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

代码语言:javascript
复制
#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所需的头文件:
代码语言:javascript
复制
#include "mbedtls/net.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
  • mbed TLS结构的创建和初始化如下所示:
代码语言:javascript
复制
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)内抽象出来,因此下面代码被简化了.

代码语言:javascript
复制
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开始实际的连接如下:

代码语言:javascript
复制
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).

代码语言:javascript
复制
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;
}

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

代码语言:javascript
复制
mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_NONE );

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

代码语言:javascript
复制
mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg );
mbedtls_ssl_conf_dbg( &conf, my_debug, stdout );

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

代码语言:javascript
复制
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来使用它.

代码语言:javascript
复制
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需要知道它需要用来发送网络流量的输入和输出功能.

代码语言:javascript
复制
mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL );

读写数据 

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

代码语言:javascript
复制
while( ( ret = write( server_fd, buf, len ) ) <= 0 )

变成

代码语言:javascript
复制
while( ( ret = mbedtls_ssl_write( &ssl, buf, len ) ) <= 0 )

从网络层读取:

代码语言:javascript
复制
ret = read( server_fd, buf, len );

变成

代码语言:javascript
复制
ret = mbedtls_ssl_read( &ssl, buf, len );

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

拆除(Teardown)

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

所以

代码语言:javascript
复制
close( server_fd );

变成

代码语言:javascript
复制
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等等.

代码语言:javascript
复制
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.

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年01月27日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 示例客户端 
  • 增加安全通信 
  • 设置
  • SSL 连接
  • SSL/TLS配置 
  • 读写数据 
  • 拆除(Teardown)
  • 服务器认证
  • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档