Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1237467
  • 博文数量: 389
  • 博客积分: 2874
  • 博客等级: 少校
  • 技术积分: 3577
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-24 10:34
文章分类

全部博文(389)

文章存档

2020年(2)

2018年(39)

2017年(27)

2016年(3)

2015年(55)

2014年(92)

2013年(54)

2012年(53)

2011年(64)

分类: 网络与安全

2013-04-12 16:33:37

ssl 探索
OpenSSL 是用于安全通信最著名的开放库。它诞生于 1998 年,源自 Eric Young 和 Tim Hudson 开发的 SSLeay 库。其他 SSL 工具包包括遵循 GNU General Public License 发行的 GNU TLS,以及 Mozilla Network Security Services(NSS)。
那么,是什么使得 OpenSSL 比 GNU TLS、Mozilla NSS 或其他所有的库都优越呢?许可是一方面因素。此外,GNS TLS(迄今为止)只支持 TLS v1.0 和 SSL v3.0 协议。
Mozilla NSS 的发行既遵循 Mozilla Public License 又遵循 GNU GPL,它允许开发人员进行选择。不过,Mozilla NSS 比 OpenSSL 大,并且需要其他外部库来对库进行编译,而 OpenSSL 是完全自包含的。与 OpenSSL 相同,大部分 NSS API 也没有文档资料。Mozilla NSS 获得了 PKCS #11 支持,该支持可以用于诸如智能卡这样的加密标志。OpenSSL 就不具备这一支持。
什么是 SSL?
SSL 是一个缩写,代表的是 Secure Sockets Layer。它是支持在 Internet 上进行安全通信的标准,并且将数据密码术集成到了协议之中。数据在离开您的计算机之前就已经被加密,然后只有到达它预定的目标后才被解密。证书和密码学算法支持了这一切的运转,使用 OpenSSL,您将有机会切身体会它们。
理论上,如果加密的数据在到达目标之前被截取或窃听,那些数据是不可能被破解的。不过,由于计算机的变化一年比一年快,而且密码翻译方法有了新的发展,因此,SSL 中使用的加密协议被破解的可能性也在增大。
可以将 SSL 和安全连接用于 Internet 上任何类型的协议,不管是 HTTP、POP3,还是 FTP。还可以用 SSL 来保护 Telnet 会话。虽然可以用 SSL 保护任何连接,但是不必对每一类连接都使用 SSL。如果连接传输敏感信息,则应使用 SSL。
什么是 OpenSSL?
OpenSSL 不仅仅是 SSL。它可以实现消息摘要、文件的加密和解密、数字证书、数字签名和随机数字。关于 OpenSSL 库的内容非常多,远不是一篇文章可以容纳的。
OpenSSL 不只是 API,它还是一个命令行工具。命令行工具可以完成与 API 同样的工作,而且更进一步,可以测试 SSL 服务器和客户机。它还让开发人员对 OpenSSL 的能力有一个认识。要获得关于如何使用 OpenSSL 命令行工具的资料。

上面是openssl的基本资料,那么如何学习ssl呢,我认为实践是学习的最好方法。

首先安装openssl库:
首先去官网下载代码包,我用的版本是openssl-1.0.0g
[code=c]
$ ./config --prefix=/usr/local --openssldir=/usr/local/openssl
$ make
$ make test
$ make install
[/code]
之后就安装成功了。


首先我们看一下openssl自带的例子程序,在源代码包里openssl-1.0.0g/demos下面有很多的例子,我们先从一个下手
在openssl-1.0.0g/demos/bio/目录下是一个ssl的服务器和客户端的例子,服务器的源代码如下:
[code=c]
#include
#include
#include
#include              //首先需要包含两个头文件
#define CERT_FILE "server.pem"           //这里声明了一个证书文件
BIO *in=NULL;                                       //BIO  :抽象IO 下面介绍
void close_up()
{
    if (in != NULL)
        BIO_free(in);
}
int main(argc,argv)
int argc;
char *argv[];
{
    char *port=NULL;
    BIO *ssl_bio,*tmp;
    SSL_CTX *ctx;                                     //ssl 上下文结构
    SSL *ssl;                                               //SSL 连接结构,可以用该 SSL 指针来检查连接信息或设置其他 SSL 参数
    char buf[512];
    int ret=1,i;
    if (argc <= 1)
        port="*:4433";                               //默认端口
    else
        port=argv[1];
    signal(SIGINT,close_up);
    SSL_load_error_strings();
#ifdef WATT32
    dbug_init();
    sock_init();
#endif
    /* Add ciphers and message digests */
    OpenSSL_add_ssl_algorithms();
    ctx=SSL_CTX_new(SSLv23_server_method());                               //分配一个上下文结构
    if (!SSL_CTX_use_certificate_file(ctx,CERT_FILE,SSL_FILETYPE_PEM))
        goto err;
    if (!SSL_CTX_use_PrivateKey_file(ctx,CERT_FILE,SSL_FILETYPE_PEM))
        goto err;
    if (!SSL_CTX_check_private_key(ctx))
        goto err;
    /* Setup server side SSL bio */
    ssl=SSL_new(ctx);
    ssl_bio=BIO_new_ssl(ctx,0);
    if ((in=BIO_new_accept(port)) == NULL) goto err;
    /* This means that when a new connection is acceptede on 'in',
     * The ssl_bio will be 'dupilcated' and have the new socket
     * BIO push into it.  Basically it means the SSL BIO will be
     * automatically setup */
    BIO_set_accept_bios(in,ssl_bio);
again:
    /* The first call will setup the accept socket, and the second
     * will get a socket.  In this loop, the first actual accept
     * will occur in the BIO_read() function. */
    if (BIO_do_accept(in) <= 0) goto err;
    for (;;)
    {
        i=BIO_read(in,buf,512);
        if (i == 0)
        {
            /* If we have finished, remove the underlying
             * BIO stack so the next time we call any function
             * for this BIO, it will attempt to do an
             * accept */
            printf("Done\n");
            tmp=BIO_pop(in);
            BIO_free_all(tmp);
            goto again;
        }
        if (i < 0) goto err;
        fwrite(buf,1,i,stdout);
        fflush(stdout);
    }
    ret=0;
err:
    if (ret)
    {
        ERR_print_errors_fp(stderr);
    }
    if (in != NULL) BIO_free(in);
    exit(ret);
    return(!ret);
}
[/code]
首先看一下源代码和注释,我们在这个基础上深入了解一些细节。
BIO 、SSL_CTX、SSL结构
BIO 是抽象IO结构,该结构封装了包括套结字和文件在内的各种类型的通信。
SSL_CTX 是ssl上下文结构,这样说可能不太好理解,SSL的文档有部分描述如下:
Then an SSL_CTX object is created as a framework to establish TLS/SSL enabled connections (see SSL_CTX_new(3)). Various options regarding certificates, algorithms etc. can be set in this object.
即SSL_CTX是一个能够建立TLS/SSL连接的框架,可以向这个框架设置证书、加密算法等信息。
SSL 类型用于保持ssl连接的结构,可以用该结构检查连接信息或设置其它SSL参数。文档中的描述:When a network connection has been created, it can be assigned to an SSL object. After the SSL object has been created using SSL_new(3), SSL_set_fd(3) or SSL_set_bio(3) can be used to associate the network connection with the object.
SSL_new() creates a new SSL structure which is needed to hold the data for a TLS/SSL connection. The new structure inherits the settings of the underlying context ctx: connection method (SSLv2/v3/TLSv1), options, verification settings, timeout settings.
SSL结构会继承SSL_CTX的设置,包括连接方法,选项,认证设置,超时设置。
然后比较显眼的是三个连续的函数SSL_CTX_use_certificate_file,SSL_CTX_use_PrivateKey_file,SSL_CTX_check_private_key。
SSL_CTX_*这一类的函数负责向SSL_CTX对象ctx内加载证书和密钥。对象ssl是以ctx为参数通过调用SSL_new函数创建的,ctx的信息会拷贝给SSL的对象ssl。所以对于ctx的修改不会影响到已经存在的SSL对象。
SSL_* 这一类的函书只是 向SSL对象加载证书和密钥。
BIO_new_ssl() 使用SSL_CTX ctx和模式参数分配一个 SSL BIO ,如果模式参数非0就是client模式.
BIO_set_accept_bios() 手册的描述是:BIO_set_accept_bios() can be used to set a chain of BIOs which will be duplicated and prepended to the chain when an incoming connection is received. This is useful if, for example, a buffering or SSL BIO is required for each connection. The chain of BIOs must not be freed after this call, they will be automatically freed when the accept BIO is freed.
结合代码里的注释,我理解为BIO *in是一个bio的链,ssl_bio是一个模板,当有新的连接,就将ssl_bio的特性复制到新的连接上,然后将新的连接添加到in这个链里。
然后调用BIO_do_accept(in)等待连接。
之后读取连接,将数据写到stdout里。

client端部分代码

[code=c]
#include
#include
#include
#include
#include

extern int errno;

int main(argc,argv)
int argc;
char *argv[];
{
    char *host;
    BIO *out;
    char buf[1024*10],*p;
    SSL_CTX *ssl_ctx=NULL;
    SSL *ssl;
    BIO *ssl_bio;
    int i,len,off,ret=1;

    if (argc <= 1)
        host="localhost:4433";
    else
        host=argv[1];

#ifdef WATT32
    dbug_init();
    sock_init();
#endif

    /* Lets get nice error messages */
    SSL_load_error_strings();

    /* Setup all the global SSL stuff */
    OpenSSL_add_ssl_algorithms();
    ssl_ctx=SSL_CTX_new(SSLv23_client_method());

    /* Lets make a SSL structure */
    ssl=SSL_new(ssl_ctx);
    SSL_set_connect_state(ssl);

    /* Use it inside an SSL BIO */
    ssl_bio=BIO_new(BIO_f_ssl());
    BIO_set_ssl(ssl_bio,ssl,BIO_CLOSE);

    /* Lets use a connect BIO under the SSL BIO */
    out=BIO_new(BIO_s_connect());
    BIO_set_conn_hostname(out,host);
    BIO_set_nbio(out,1);
    out=BIO_push(ssl_bio,out);

    p="GET / HTTP/1.0\r\n\r\n";
    len=strlen(p);

    off=0;
    for (;;)
    {
        i=BIO_write(out,&(p[off]),len);
        if (i <= 0)
        {
            if (BIO_should_retry(out))
            {
                fprintf(stderr,"write DELAY\n");
                sleep(1);
                continue;
            }
            else
            {
                goto err;
            }
        }
        off+=i;
        len-=i;
        if (len <= 0) break;
    }

    for (;;)
    {
        i=BIO_read(out,buf,sizeof(buf));
        if (i == 0) break;
        if (i < 0)
        {
            if (BIO_should_retry(out))
            {
                fprintf(stderr,"read DELAY\n");
                sleep(1);
                continue;
            }
            goto err;
        }
        fwrite(buf,1,i,stdout);
    }

    ret=1;

    if (0)
    {
err:
        if (ERR_peek_error() == 0) /* system call error */
        {
            fprintf(stderr,"errno=%d ",errno);
            perror("error");
        }
        else
            ERR_print_errors_fp(stderr);
    }
    BIO_free_all(out);
    if (ssl_ctx != NULL) SSL_CTX_free(ssl_ctx);
    exit(!ret);
    return(ret);
}
[/code]

首先创建了一个client类型的ssl_ctx对象和对应的ssl对象。
       When the SSL_CTX object was created with SSL_CTX_new(3), it was either
       assigned a dedicated client method, a dedicated server method, or a
       generic method, that can be used for both client and server connec-
       tions. (The method might have been changed with SSL_CTX_set_ssl_ver-
       sion(3) or SSL_set_ssl_method().)

       When beginning a new handshake, the SSL engine must know whether it
       must call the connect (client) or accept (server) routines. Even
       though it may be clear from the method chosen, whether client or
       server mode was requested, the handshake routines must be explicitly
       set.

       When using the SSL_connect(3) or SSL_accept(3) routines, the correct
       handshake routines are automatically set. When performing a transpar-
       ent negotiation using SSL_write(3) or SSL_read(3), the handshake rou-
       tines must be explicitly set in advance using either SSL_set_con-
       nect_state() or SSL_set_accept_state().

SSL_set_connect_state() 将ssl对象设置为客户端模式。

       Some BIOs (such as memory BIOs) can be used immediately after calling
       BIO_new(). Others (such as file BIOs) need some additional initializa-
       tion, and frequently a utility function exists to create and initial-
       ize such BIOs.

       If BIO_free() is called on a BIO chain it will only free one BIO
       resulting in a memory leak.

       Calling BIO_free_all() a single BIO has the same effect as calling
       BIO_free() on it other than the discarded return value.

       Normally the type argument is supplied by a function which returns a
       pointer to a BIO_METHOD. There is a naming convention for such func-
       tions: a source/sink BIO is normally called BIO_s_*() and a filter BIO
       BIO_f_*();

       BIO_f_ssl() returns the SSL BIO method. This is a filter BIO which is
       a wrapper round the OpenSSL SSL routines adding a BIO "flavour" to SSL
       I/O.

       I/O performed on an SSL BIO communicates using the SSL protocol with
       the SSLs read and write BIOs. If an SSL connection is not established
       then an attempt is made to establish one on the first I/O call.

       If a BIO is appended to an SSL BIO using BIO_push() it is automati-
       cally used as the SSL BIOs read and write BIOs.

       Calling BIO_reset() on an SSL BIO closes down any current SSL connec-
       tion by calling SSL_shutdown(). BIO_reset() is then sent to the next
       BIO in the chain: this will typically disconnect the underlying trans-
       port.  The SSL BIO is then reset to the initial accept or connect
       state.

       If the close flag is set when an SSL BIO is freed then the internal
       SSL structure is also freed using SSL_free().

ssl_bio=BIO_new(BIO_f_ssl()),这里的参数是 BIO_f_ssl()函数的返回值,BIO_f_ssl 返回 SSL BIO method。这是一个过滤型BIO,它封装了OpenSSL SSL routines,他向SSL IO增加了BIO的一些有用的特性.

#define BIO_set_ssl(b,ssl,c)   BIO_ctrl(b,BIO_C_SET_SSL,c,(char *)ssl)
 BIO_set_ssl() sets the internal SSL pointer of BIO b to ssl using the
       close flag c.
BIO_set_ssl(ssl_bio,ssl,BIO_CLOSE); 使用 BIO_CLOSE 标志将ssl_bio的内部SSL 指针设置为ssl。BIO_CLOSE 标志的作用是如果 ssl_bio 释放了,那么通过调用SSL_free()将内部的ssl指针指向的结构体释放。

       BIO_s_connect() returns the connect BIO method. This is a wrapper
       round the platform’s TCP/IP socket connection routines.

       Using connect BIOs, TCP/IP connections can be made and data trans-
       ferred using only BIO routines. In this way any platform specific
       operations are hidden by the BIO abstraction.

       Read and write operations on a connect BIO will perform I/O on the
       underlying connection. If no connection is established and the port
       and hostname (see below) is set up properly then a connection is
       established first.

       Connect BIOs support BIO_puts() but not BIO_gets().

       If the close flag is set on a connect BIO then any active connection
       is shutdown and the socket closed when the BIO is freed.

       Calling BIO_reset() on a connect BIO will close any active connection
       and reset the BIO into a state where it can connect to the same host
       again.

BIO_s_connect() 返回 connect BIO method,是对tcp/ip socket客户端的封装。

阅读(1603) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~