使用+OpenSSL+API+进行安全编程1.docx

上传人:b****6 文档编号:8550877 上传时间:2023-01-31 格式:DOCX 页数:11 大小:24.08KB
下载 相关 举报
使用+OpenSSL+API+进行安全编程1.docx_第1页
第1页 / 共11页
使用+OpenSSL+API+进行安全编程1.docx_第2页
第2页 / 共11页
使用+OpenSSL+API+进行安全编程1.docx_第3页
第3页 / 共11页
使用+OpenSSL+API+进行安全编程1.docx_第4页
第4页 / 共11页
使用+OpenSSL+API+进行安全编程1.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

使用+OpenSSL+API+进行安全编程1.docx

《使用+OpenSSL+API+进行安全编程1.docx》由会员分享,可在线阅读,更多相关《使用+OpenSSL+API+进行安全编程1.docx(11页珍藏版)》请在冰豆网上搜索。

使用+OpenSSL+API+进行安全编程1.docx

使用+OpenSSL+API+进行安全编程1

使用OpenSSLAPI进行安全编程

(1)

学习如何使用OpenSSL——用于安全通信的最著名的开放库——的API有些强人所难,因为其文档并不完全。

您可以通过本文中的提示补充这方面的知识,并驾驭该API。

在建立基本的连接之后,就可以查看如何使用OpenSSL的BIO库来建立安全连接和非安全连接。

与此同时,您还会学到一些关于错误检测的知识。

OpenSSLAPI的文档有些含糊不清。

因为还没有多少关于OpenSSL使用的教程,所以对初学者来说,在应用程序中使用它可能会有一些困难。

那么怎样才能使用OpenSSL实现一个基本的安全连接呢?

本教程将帮助您解决这个问题。

学习如何实现OpenSSL的困难部分在于其文档的不完全。

不完全的API文档通常会妨碍开发人员使用该API,而这通常意味着它注定要失败。

但OpenSSL仍然很活跃,而且正逐渐变得强大。

这是为什么?

OpenSSL是用于安全通信的最著名的开放库。

在google中搜索“SSLlibrary”得到的返回结果中,列表最上方就是OpenSSL。

它诞生于1998年,源自EricYoung和TimHudson开发的SSLeay库。

其他SSL工具包包括遵循GNUGeneralPublicLicense发行的GNUTLS,以及MozillaNetworkSecurityServices(NSS)(请参阅本文后面的参考资料,以获得其他信息)。

那么,是什么使得OpenSSL比GNUTLS、MozillaNSS或其他所有的库都优越呢?

许可是一方面因素(请参阅参考资料)。

此外,GNSTLS(迄今为止)只支持TLSv1.0和SSLv3.0协议,仅此而已。

MozillaNSS的发行既遵循MozillaPublicLicense又遵循GNUGPL,它允许开发人员进行选择。

不过,MozillaNSS比OpenSSL大,并且需要其他外部库来对库进行编译,而OpenSSL是完全自包含的。

与OpenSSL相同,大部分NSSAPI也没有文档资料。

MozillaNSS获得了PKCS#11支持,该支持可以用于诸如智能卡这样的加密标志。

OpenSSL就不具备这一支持。

先决条件

要充分理解并利用本文,您应该:

精通C编程。

熟悉Internet通信和支持Internet的应用程序的编写。

并不绝对要求您熟悉SSL,因为稍后将给出对SLL的简短说明;不过,如果您希望得到详细论述SSL的文章的链接,请参阅参考资料部分。

拥有密码学方面的知识固然好,但这并不是必需的。

什么是SSL?

SSL是一个缩写,代表的是SecureSocketsLayer。

它是支持在Internet上进行安全通信的标准,并且将数据密码术集成到了协议之中。

数据在离开您的计算机之前就已经被加密,然后只有到达它预定的目标后才被解密。

证书和密码学算法支持了这一切的运转,使用OpenSSL,您将有机会切身体会它们。

理论上,如果加密的数据在到达目标之前被截取或窃听,那些数据是不可能被破解的。

不过,由于计算机的变化一年比一年快,而且密码翻译方法有了新的发展,因此,SSL中使用的加密协议被破解的可能性也在增大。

可以将SSL和安全连接用于Internet上任何类型的协议,不管是HTTP、POP3,还是FTP。

还可以用SSL来保护Telnet会话。

虽然可以用SSL保护任何连接,但是不必对每一类连接都使用SSL。

如果连接传输敏感信息,则应使用SSL。

什么是OpenSSL?

OpenSSL不仅仅是SSL。

它可以实现消息摘要、文件的加密和解密、数字证书、数字签名和随机数字。

关于OpenSSL库的内容非常多,远不是一篇文章可以容纳的。

OpenSSL不只是API,它还是一个命令行工具。

命令行工具可以完成与API同样的工作,而且更进一步,可以测试SSL服务器和客户机。

它还让开发人员对OpenSSL的能力有一个认识。

要获得关于如何使用OpenSSL命令行工具的资料,请参阅参考资料部分。

您需要什么

首先需要的是最新版本的OpenSSL。

查阅参考资料部分,以确定从哪里可以获得最新的可以自己编译的源代码,或者最新版本的二进制文件(如果您不希望花费时间来编译的话)。

不过,为了安全起见,我建议您下载最新的源代码并自己编译它。

二进制版本通常是由第三方而不是由OpenSSL的开发人员来编译和发行的。

一些Linux的发行版本附带了OpenSSL的二进制版本,对于学习如何使用OpenSSL库来说,这足够了;不过,如果您打算去做一些实际的事情,那么一定要得到最新的版本,并保持该版本一直是最新的。

对于以RPM形式安装的Linux发行版本(RedHat、Mandrake等),建议您通过从发行版本制造商那里获得RPM程序包来更新您的OpenSSL发行版本。

出于安全方面的原因,建议您使用最新版本的发行版本。

如果您的发行版本不能使用最新版本的OpenSSL,那么建议您只覆盖库文件,不要覆盖可执行文件。

OpenSSL附带的FAQ文档中包含了有关这方面的细节。

还要注意的是,OpenSSL并没有在所有的平台上都获得官方支持。

虽然制造商已经尽力使其能够跨平台兼容,但仍然存在OpenSSL不能用于您的计算机和/或操作系统的可能。

请参阅OpenSSL的Web站点(参考资料中的链接),以获得关于哪些平台可以得到支持的信息。

如果想使用OpenSSL来生成证书请求和数字证书,那么必须创建一个配置文件。

在OpenSSL程序包的apps文件夹中,有一个名为f的可用模板文件。

我不会对该文件进行讨论,因为这不在本文要求范围之内。

不过,该模板文件有一些非常好的注释,而且如果在Internet上搜索,您可以找到很多讨论修改该文件的教程。

头文件和初始化

本教程所使用的头文件只有三个:

ssl.h、bio.h和err.h。

它们都位于openssl子目录中,而且都是开发您的项目所必需的。

要初始化OpenSSL库,只需要三个代码行即可。

清单1中列出了所有内容。

其他的头文件和/或初始化函数可能是其他一些功能所必需的。

清单1.必需的头文件

/*OpenSSLheaders*/

#include"openssl/bio.h"

#include"openssl/ssl.h"

#include"openssl/err.h"

/*InitializingOpenSSL*/

SSL_load_error_strings();

ERR_load_BIO_strings();

OpenSSL_add_all_algorithms();

建立非安全连接

不管连接是安全的还是不安全的,OpenSSL都使用了一个名为BIO的抽象库来处理包括文件和套接字在内的各种类型的通信。

您还可以将OpenSSL设置成为一个过滤器,比如用于UU或Base64编码的过滤器。

在这里对BIO库进行全面说明有点麻烦,所以我将根据需要一点一点地介绍它。

首先,我将向您展示如何建立一个标准的套接字连接。

相对于使用BSD套接字库,该操作需要的代码行更少一些。

在建立连接(无论安全与否)之前,要创建一个指向BIO对象的指针。

这类似于在标准C中为文件流创建FILE指针。

清单2.指针

BIO*bio;

打开连接

创建新的连接需要调用BIO_new_connect。

您可以在同一个调用中同时指定主机名和端口号。

也可以将其拆分为两个单独的调用:

一个是创建连接并设置主机名的BIO_new_connect调用,另一个是设置端口号的BIO_set_conn_port(或者BIO_set_conn_int_port)调用。

不管怎样,一旦BIO的主机名和端口号都已指定,该指针会尝试打开连接。

没有什么可以影响它。

如果创建BIO对象时遇到问题,指针将会是NULL。

为了确保连接成功,必须执行BIO_do_connect调用。

清单3.创建并打开连接

bio=BIO_new_connect("hostname:

port");

if(bio==NULL)

{

/*Handlethefailure*/

}

if(BIO_do_connect(bio)<=0)

{

/*Handlefailedconnection*/

}

在这里,第一行代码使用指定的主机名和端口创建了一个新的BIO对象,并以所示风格对该对象进行格式化。

例如,如果您要连接到的80端口,那么该字符串将是:

80。

调用BIO_do_connect检查连接是否成功。

如果出错,则返回0或-1。

与服务器进行通信

不管BIO对象是套接字还是文件,对其进行的读和写操作都是通过以下两个函数来完成的:

BIO_read和BIO_write。

很简单,对吧?

精彩之处就在于它始终如此。

BIO_read将尝试从服务器读取一定数目的字节。

它返回读取的字节数、0或者-1。

在受阻塞的连接中,该函数返回0,表示连接已经关闭,而-1则表示连接出现错误。

在非阻塞连接的情况下,返回0表示没有可以获得的数据,返回-1表示连接出错。

可以调用BIO_should_retry来确定是否可能重复出现该错误。

清单4.从连接读取

intx=BIO_read(bio,buf,len);

if(x==0)

{

/*Handleclosedconnection*/

}

elseif(x<0)

{

if(!

BIO_should_retry(bio))

{

/*Handlefailedreadhere*/

}

/*Dosomethingtohandletheretry*/

}

BIO_write会试着将字节写入套接字。

它将返回实际写入的字节数、0或者-1。

同BIO_read,0或-1不一定表示错误。

BIO_should_retry是找出问题的途径。

如果需要重试写操作,它必须使用和前一次完全相同的参数。

清单5.写入到连接

if(BIO_write(bio,buf,len)<=0)

{

if(!

BIO_should_retry(bio))

{

/*Handlefailedwritehere*/

}

/*Dosomethingtohandletheretry*/

}

关闭连接

关闭连接也很简单。

您可以使用以下两种方式之一来关闭连接:

BIO_reset或BIO_free_all。

如果您还需要重新使用对象,那么请使用第一种方式。

如果您不再重新使用它,则可以使用第二种方式。

BIO_reset关闭连接并重新设置BIO对象的内部状态,以便可以重新使用连接。

如果要在整个应用程序中使用同一对象,比如使用一台安全的聊天客户机,那么这样做是有益的。

该函数没有返回值。

BIO_free_all所做正如其所言:

它释放内部结构体,并释放所有相关联的内存,其中包括关闭相关联的套接字。

如果将BIO嵌入于一个类中,那么应该在类的析构函数中使用这个调用。

清单6.关闭连接

/*Toreusetheconnection,usethisline*/

BIO_reset(bio);

/*Tofreeitfrommemory,usethisline*/

BIO_free_all(bio);

 

建立安全连接

现在需要给出建立安全连接需要做哪些事情。

惟一要改变的地方就是建立并进行连接。

其他所有内容都是相同的。

安全连接要求在连接建立后进行握手。

在握手过程中,服务器向客户机发送一个证书,然后,客户机根据一组可信任证书来核实该证书。

它还将检查证书,以确保它没有过期。

要检验证书是可信任的,需要在连接建立之前提前加载一个可信任证书库。

只有在服务器发出请求时,客户机才会向服务器发送一个证书。

该过程叫做客户机认证。

使用证书,在客户机和服务器之间传递密码参数,以建立安全连接。

尽管握手是在建立连接之后才进行的,但是客户机或服务器可以在任何时刻请求进行一次新的握手。

参考资料部分中列出的Netscasp文章和RFC2246,对握手以及建立安全连接的其他方面的知识进行了更详尽的论述。

为安全连接进行设置

为安全连接进行设置要多几行代码。

同时需要有另一个类型为SSL_CTX的指针。

该结构保存了一些SSL信息。

您也可以利用它通过BIO库建立SSL连接。

可以通过使用SSL方法函数调用SSL_CTX_new来创建这个结构,该方法函数通常是SSLv23_client_method。

还需要另一个SSL类型的指针来保持SSL连接结构(这是短时间就能完成的一些连接所必需的)。

以后还可以用该SSL指针来检查连接信息或设置其他SSL参数。

清单7.设置SSL指针

SSL_CTX*ctx=SSL_CTX_new(SSLv23_client_method());

SSL*ssl;

加载可信任证书库

在创建上下文结构之后,必须加载一个可信任证书库。

这是成功验证每个证书所必需的。

如果不能确认证书是可信任的,那么OpenSSL会将证书标记为无效(但连接仍可以继续)。

OpenSSL附带了一组可信任证书。

它们位于源文件树的certs目录中。

不过,每个证书都是一个独立的文件——也就是说,需要单独加载每一个证书。

在certs目录下,还有一个存放过期证书的子目录。

试图加载这些证书将会出错。

如果您愿意,可以分别加载每一个文件,但为了简便起见,最新的OpenSSL发行版本的可信任证书通常存放在源代码档案文件中,这些档案文件位于名为“TrustStore.pem”的单个文件中。

如果已经有了一个可信任证书库,并打算将它用于特定的项目中,那么只需使用您的文件替换清单8中的“TrustStore.pem”(或者使用单独的函数调用将它们全部加载)即可。

可以调用SSL_CTX_load_verify_locations来加载可信任证书库文件。

这里要用到三个参数:

上下文指针、可信任库文件的路径和文件名,以及证书所在目录的路径。

必须指定可信任库文件或证书的目录。

如果指定成功,则返回1,如果遇到问题,则返回0。

清单8.加载信任库

if(!

SSL_CTX_load_verify_locations(ctx,"/path/to/TrustStore.pem",NULL))

{

/*Handlefailedloadhere*/

}

如果打算使用目录存储可信任库,那么必须要以特定的方式命名文件。

OpenSSL文档清楚地说明了应该如何去做,不过,OpenSSL附带了一个名为c_rehash的工具,它可以将文件夹配置为可用于SSL_CTX_load_verify_locations的路径参数。

清单9.配置证书文件夹并使用它

/*Usethisatthecommandline*/

c_rehash/path/to/certfolder

/*thencallthisfromwithintheapplication*/

if(!

SSL_CTX_load_verify_locations(ctx,NULL,"/path/to/certfolder"))

{

/*Handleerrorhere*/

}

为了指定所有需要的验证证书,您可以根据需要命名任意数量的单独文件或文件夹。

您还可以同时指定文件和文件夹。

创建连接

将指向SSL上下文的指针作为惟一参数,使用BIO_new_ssl_connect创建BIO对象。

还需要获得指向SSL结构的指针。

在本文中,只将该指针用于SSL_set_mode函数。

而这个函数是用来设置SSL_MODE_AUTO_RETRY标记的。

使用这个选项进行设置,如果服务器突然希望进行一次新的握手,那么OpenSSL可以在后台处理它。

如果没有这个选项,当服务器希望进行一次新的握手时,进行读或写操作都将返回一个错误,同时还会在该过程中设置retry标记。

清单10.设置BIO对象

bio=BIO_new_ssl_connect(ctx);

BIO_get_ssl(bio,&ssl);

SSL_set_mode(ssl,SSL_MODE_AUTO_RETRY);

设置SSL上下文结构之后,就可以创建连接了。

主机名是使用BIO_set_conn_hostname函数设置的。

主机名和端口的指定格式与前面的相同。

该函数还可以打开到主机的连接。

为了确认已经成功打开连接,必须执行对BIO_do_connect的调用。

该调用还将执行握手来建立安全连接。

清单11.打开安全连接

/*Attempttoconnect*/

BIO_set_conn_hostname(bio,"hostname:

port");

/*Verifytheconnectionopenedandperformthehandshake*/

if(BIO_do_connect(bio)<=0)

{

/*Handlefailedconnection*/

}

连接建立后,必须检查证书,以确定它是否有效。

实际上,OpenSSL为我们完成了这项任务。

如果证书有致命的问题(例如,哈希值无效),那么将无法建立连接。

但是,如果证书的问题并不是致命的(当它已经过期或者尚不合法时),那么仍可以继续使用连接。

可以将SSL结构作为惟一参数,调用SSL_get_verify_result来查明证书是否通过了OpenSSL的检验。

如果证书通过了包括信任检查在内的OpenSSL的内部检查,则返回X509_V_OK。

如果有地方出了问题,则返回一个错误代码,该代码被记录在命令行工具的verify选项下。

应该注意的是,验证失败并不意味着连接不能使用。

是否应该使用连接取决于验证结果和安全方面的考虑。

例如,失败的信任验证可能只是意味着没有可信任的证书。

连接仍然可用,只是需要从思想上提高安全意识。

清单12.检查证书是否有效

if(SSL_get_verify_result(ssl)!

=X509_V_OK)

{

/*Handlethefailedverification*/

}

这就是所需要的全部操作。

通常,与服务器进行通信都要使用BIO_read和BIO_write。

并且只需调用BIO_free_all或BIO_reset,就可以关闭连接,具体调用哪一个方法取决于是否重用BIO。

必须在结束应用程序之前的某个时刻释放SSL上下文结构。

可以调用SSL_CTX_free来释放该结构。

清单13.清除SSL上下文

SSL_CTX_free(ctx);

 

错误检测

显然OpenSSL抛出了某种类型的错误。

这意味着什么?

首先,您需要得到错误代码本身;ERR_get_error可以完成这项任务;然后,需要将错误代码转换为错误字符串,它是一个指向由SSL_load_error_strings或ERR_load_BIO_strings加载到内存中的永久字符串的指针。

可以在一个嵌套调用中完成这项操作。

表1略述了从错误栈检索错误的方法。

清单24展示了如何打印文本字符串中的最后一个错误信息。

表1.从栈中检索错误

ERR_reason_error_string

返回一个静态字符串的指针,然后可以将字符串显示在屏幕上、写入文件,或者以任何您希望的方式进行处理

ERR_lib_error_string

指出错误发生在哪个库中

ERR_func_error_string

返回导致错误的OpenSSL函数

清单14.打印出最后一个错误

printf("Error:

%s\n",ERR_reason_error_string(ERR_get_error()));

您还可以让库给出预先格式化了的错误字符串。

可以调用ERR_error_string来得到该字符串。

该函数将错误代码和一个预分配的缓冲区作为参数。

而这个缓冲区必须是256字节长。

如果参数为NULL,则OpenSSL会将字符串写入到一个长度为256字节的静态缓冲区中,并返回指向该缓冲区的指针。

否则,它将返回您给出的指针。

如果您选择的是静态缓冲区选项,那么在下一次调用ERR_error_string时,该缓冲区会被覆盖。

清单15.获得预先格式化的错误字符串

printf("%s\n",ERR_error_string(ERR_get_error(),NULL));

您还可以将整个错误队列转储到文件或BIO中。

可以通过ERR_print_errors或ERR_print_errors_fp来实现这项操作。

队列是以可读格式被转储的。

第一个函数将队列发送到BIO,第二个函数将队列发送到FILE。

字符串格式如下(引自OpenSSL文档):

[pid]:

error:

[errorcode]:

[libraryname]:

[functionname]:

[reasonstring]:

[filename]:

[line]:

[optionaltextmessage]

其中,[pid]是进程ID,[errorcode]是一个8位十六进制代码,[filename]是OpenSSL库中的源代码文件,[line]是源文件中的行号。

清单16.转储错误队列

ERR_print_errors_fp(FILE*);

ERR_print_errors(BIO*);

 

开始做吧

使用OpenSSL创建基本的连接并不困难,但是,当试着确定该如何去做时,文档可能是一个小障碍。

本文向您介绍了一些基本概念,但OpenSSL还有很多灵活之处有待发掘,而且您还可能需要一些高级设置,以便项目能够充分利用SSL的功能。

本文中有两个样例。

一个样例展示了到的非安全连接,另一个则展示了到的安全SSL连接。

两者都是连接到服务器并下载其主页。

它们没有进行任何安全检查,而且库中的所有设置都是默认值——作为本文的一部分,应该只将这些用于教学目的。

在任何支持的平台上,源代码的编译都应该是非常容易的,不过我建议您使用最新版本的OpenS

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1