mbed TLS 简明教程(二)

译者:远方的自由 转载请注明出处: 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.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android 研究

OKHttp源码解析(二):"前戏"——HTTP的那些事

本篇文章为OkHttp的"前戏"篇,主要讲解关于http协议的一些基础知识。主要内容如下:

39260
来自专栏小樱的经验随笔

【干货分享】常用端口服务对照表

端口:0 服务:Reserved 说明:通常用于分析操作系统。这一方法能够工作是因为在一些系统中“0”是无效端口,当你试图使用通常的闭合端口连接它时将产生不...

46350
来自专栏cloudskyme

跟我一起云计算(4)——lucene

了解lucene的基本概念 这一部分可以参考我以前写的博客: http://www.cnblogs.com/skyme/tag/lucene/ lucene是什...

34960
来自专栏沈唁志

修改Git全部Commit提交记录的用户名Name和邮箱Email

这两天好不容易有空提交一下开源代码了,结果在公司提交的代码有记录,但是没有绿色,延迟?真的要凉

2K20
来自专栏有趣的django

Django REST framework+Vue 打造生鲜超市(六) 七、用户登录与手机注册

七、用户登录与手机注册 7.1.drf的token (1)INSTALL_APP中添加 INSTALLED_APPS = ( ... 'rest...

2.9K80
来自专栏【转载】DRF+Vue+Mysql_生鲜超市系统

七、用户登录与手机注册

转载原文:https://cloud.tencent.com/developer/article/1097993

94910
来自专栏信安之路

无线渗透(下)—企业级WPA破解

21200
来自专栏云计算教程系列

如何在Debian 9中为Apache创建自签名SSL证书

TLS或传输层安全性及其前身SSL(代表安全套接字层)是用于将正常流量包装在受保护的加密包装中的Web协议。

20820
来自专栏北京马哥教育

吐血整理所有常用端口,不全你来打我!

作者:ADreamClusive 来源: http://blog.csdn.net/u013943420/article/details/65938696 大家...

450130
来自专栏散尽浮华

分布式监控系统Zabbix-3.0.3--短信报警设置

前面已分别介绍了zabbix的邮件、微信报警设置,这些都是手机在有网络时才能收到报警信息,那如果手机没有网的情况下怎么办,这就需要考虑使用短信接口报警了。当服务...

48080

扫码关注云+社区

领取腾讯云代金券