4.6使用引擎
OpenSSL具有对内嵌密码加速的支持。使用引擎对象模型,一个应用程序可以得到一个可变化的参考模型,此参考模型在下面的描述中大部分是一个硬件装置。这种支持是内嵌于OpenSSL的0.9.6版本中,此版本中就出现了引擎这个名字;这种支持将在OpenSSL的主分支中得到限制,从它的0.9.7这个版本开始。0.9.7这个版本将具有更健壮的特性用于对引擎包的详细叙述,与此同时,0.9.6的引擎包括一些简易的功能用于建立一个引擎对象。这些功能在书写过程中看上去并没有变化。如果有变化,我们将根据有关的信息升级我们的网站。大概的思想很简单:我们寻找一个对象用来代表我们希望使用的硬件类型,然后我们告诉OpenSSL使用我们所选择的设备。例4-17提供了一小段程序代码,用例子来解释我们如何实现这种操作。
例4-17 允许使用硬件引擎
ENGINE *e;
if (!(e = ENGINE_by_id("cswift")))
fprintf(stderr, "Error finding specified ENGINE\n");
else if (!ENGINE_set_default(e, ENGINE_METHOD_ALL))
fprintf(stderr, "Error using ENGINE\n");
else
fprintf(stderr, "Engine successfully enabled\n")
这个函数称作ENGINE_by_id,他将在可以使用的嵌入是方法中查询并执行,而且返回一个引擎对象。这种函数中单一的参数应该成为连接我们接下来的执行过程的等同体。表格4-2显示了可以使用的方法用来支持硬件和软件编码。
表格4-2。支持硬件和软件的引擎
openssl 引擎使用普通的嵌入是功能用于加密功能。
这是默认的。
openbsd_dev_crypto建立在开放BSD操作系统上,这个引擎建使用内嵌于操作系统的核心层次的密码术。
Cswift用于硬件密码加速。
Chil用于nCipher CHIL硬件加速。
Atalla用于康柏Atalla硬件加速。
Nuron用于Nuron硬件加速。
Ubsec用于
Broadcom uBSec硬件加速。
Aep用于Aep硬件加速。
Sureware用于SureWare硬件加速。
我们查询得到的引擎对象应该用于通知ENGINE_set_default来允许加密功能区运用具体引擎。第二个参数允许我们详细说明我们允许引擎工作的限制。例如,如果我们有一个只能工作于RSA上的引擎,发一个如例4-17的请求,则此请求将把RSA交由引擎处理。另一方面,如果我们请求ENGINE_set_default用我们的RSA引擎同时请求ENGINE_METHOD_DSA,OpenSSL将不允许任何引擎用于加密请求,因为这个标志允许引擎只工作于DSA功能。表4-3提供了一个我们使用限制的完全列表。他们可以于逻辑OR操作绑定在一起。
表4-3,ENGINE_set_default的标志位
标志位描述
ENGINE_METHOD_RSA限制引擎仅使用于RSA操作。
ENGINE_METHOD_DSA限制引擎仅使用于DSA操作。
ENGINE_METHOD_DH限制引擎仅使用于DH操作。
ENGINE_METHOD_RAND限制引擎仅使用于随机数操作。
ENGINE_METHOD_CIPHERS限制引擎仅使用于平衡零操作。
ENGINE_METHOD_DIGESTS限制引擎仅使用于消化操作。
ENGINE_METHOD_ALL允许OpenSSL使用任何以上提到的执行功能。
除去设置默认引擎,引擎对象都可以典型的应用于其他几个地方OpenSSL的0.9.7这个版本。例如,EVP_EncryptInit函数可以免于错误而且可以被EVP_EncryptInit_ex这个函数所代替。这里的“ex”函数带着一个附加的参数:引擎对象。粗略说来,这些替换函数可以赋给引擎对象以一个空值,我们就可以让OpenSSL使用默认的引擎。如果我们已经请求ENGINE_set_default,再次请求则默认的引擎就已改变;如果没有这些请求,则嵌入式软件将被应用。
应用这些新的“ex”函数是允许更多的好的操作应用于每个请求后加密仪器。这对于我们拥有多个密码加速器的情况尤其有用,而且我们可以根据应用程序来使用他们。
第五章 SSL/TLS 编程
OpenSSL库的主要特征是它的安全接口层(SSL)和传输层的安全性(TLS)协议。起初是由网景公司开发应用于保护网络交易的安全,这个协议如今成为一个通用的线程交流的安全保证。网景公司第一个有关SSL的公开版本时被我们现在称为SSL2的版本。
5.1SSL程序设计
OpenSSL是美国石油协会为 SSL开发的程序。如第一章所讨论,SSL 在完成它的安全目标可能是低效的。 这些因素混合使用对于开发者是一件困难的工作。 在松开实现无虑的计画秘密的希望, 我们攻击问题在三个步骤。 在每个步骤,开发者必须提供一些申请- 特效药知识给确定 SSL 做它的工作。 举例来说,被使被一个开发者一高度地能共处的选择设计圈套浏览器将会不同於被一高度地无虑的伺候器的一个开发者做的那人申请。
当实现一位 SSL 客户的时候 , 下面的步骤为开发者提供一个型板遵从或伺候器。我们将会由于一个小的例子开始而且在它之上建立。 这个例子将考虑到我们的满足直到所有的步骤都被想到。 在每个步骤中,我们将会介绍一美国石油协会的小剂量; 在所有的步骤之後,开发者应该可以想过设计一个 SSL 使的申请能够更加清楚地。 完成这些步骤不是结束那道路,然而。 为了要向许多申请的需求演说,我们需要比较进一步去而且调查 美国石油协会的进一步的特征
5.1.1安全应用
我们将使用二个非常简单的申请: 从客户到控制台发回数据的客户和伺候器。我们的目标要增加两个申请以便他们能运行他们的工作。 换句话说,我们将会贯彻每个程序来证明连接。 当我们通过路径生成SSL 程序译文时,我们认为每一个开发者必须做这项工作在这一时期。
在那里总共有四个文件: common.h,common.c ,client.c 和 server.c。译码在例5-1种给出。5-4中也有我们使用的译码在例4-2中已给出,所以我们可以多线程操作,例如UNIX系统,我们可以继续使用POSIX线程。
例5-1.Common.h
1 #include <openssl/bio.h>
2 #include <openssl/err.h>
3 #include <openssl/rand.h>
4 #include <openssl/ssl.h>
5 #include <openssl/x509v3.h>
6
7 #ifndef WIN32
8 #include <pthread.h>
9 #define THREAD_CC
10 #define THREAD_TYPE pthread_t
11 #define THREAD_CREATE(tid, entry, arg) pthread_create(&(tid), NULL, 12 (entry), (arg))
13 #else
14 #include <windows.h>
15 #define THREAD_CC _ _cdecl
16 #define THREAD_TYPE DWORD
17 #define THREAD_CREATE(tid, entry, arg) do { _beginthread((entry), 0,
(arg)); 18 (tid) =
GetCurrentThreadId(); 19 } while (0)
20 #endif
21
22 #define PORT "6001"
23 #define SERVER "splat.zork.org"
24 #define CLIENT "shell.zork.org"
25
26 #define int_error(msg) handle_error(__FILE__, __LINE_ _, msg)
27 void handle_error(const char *file, int lineno, const char *msg);
28
29 void init_OpenSSL(void);
例5-2,common.c,给错误功能报告handle_error下一个定义。错误处理在这个应用例子中有一点麻烦,你会更喜欢在更友好的界面中使用自己的错误处理。总之,它不适用于所有的错误处理,common.c文件也给多现程OpenSSL定义初始化函数,初始化库,并调用错误字符串,当错误发生和打印错误栈时请求SSL_load_error_strings,我们获取可以读懂的错误信息。从内存调用这些诊断信息。通常,在工作译码错误中调用这些诊断信息是个好办法。
正如我们建立在SSL支持中的一样,common.c将通过用户和服务器获取执行函数,common.H将获取数据原形。
例5-2.common.c
1 #include "common.h"
2
3 void handle_error(const char *file, int lineno, const char *msg)
4 {
5 fprintf(stderr, "** %s:%i %s\n", file, lineno, msg);
6 ERR_print_errors_fp(stderr);
7 exit(-1);
8 }
9
10 void init_OpenSSL(void)
11 {
12 if (!THREAD_setup() || ! SSL_library_init())
13 {
14 fprintf(stderr, "** OpenSSL initialization failed!\n");
15 exit(-1);
16 }
17 SSL_load_error_strings();
18 }
大量的客户应用在例5-3,client.c。在更高的层次上,它产生了一个面向6001服务端口的连接,正像在common.h.中指定的。一旦连接建立起来,在EOF到达前,它从stdin中读取数据。正如从内从中读取数据,发送给连接的服务器一样。注意这一点,虽然我们使用OpenSSL进行接口通信,我们仍然可以使用SSL协议。第27-29行建立了一个新的从BIO_s_connect返回给BIO_METHOD的BIO对象;BIO_new_connect是一个完成任务的简单函数。如果没有错误发生,31-32行实际上做一个TCP连接并检查错误。当一个连接成功,do_client_loop持续读取数据并往stdin中写入数据。如果错误在写入时发生或者EOF在读取控制台时接收,这个函数退出,程序结束。
例5-3 client.c
1 #include "common.h"
2
3 void do_client_loop(BIO *conn)
4 {
5 int err, nwritten;
6 char buf[80];
7
8 for (;;)
9 {
10 if (!fgets(buf, sizeof(buf), stdin))
11 break;
12 for (nwritten = 0; nwritten < sizeof(buf); nwritten +=
err)
13 {
14 err = BIO_write(conn, buf + nwritten, strlen(buf) -
nwritten);
15 if (err <= 0)
16 return;
17 }
18 }
19 }
20
21 int main(int argc, char *argv[])
22 {
23 BIO *conn;
24
25 init_OpenSSL();
26
27 conn = BIO_new_connect(SERVER ":" PORT);
28 if (!conn)
29 int_error("Error creating connection BIO");
30
31 if (BIO_do_connect(conn) <= 0)
32 int_error("Error connecting to remote machine");
33
34 fprintf(stderr, "Connection opened\n");
35 do_client_loop(conn);
36 fprintf(stderr, "Connection closed\n");
37
38 BIO_free(conn);
39 return 0;
40 }
服务器应用在例5-4中,server.c,在许多方面不同于其他的用户程序。当我们使用了初始化功能之后,它引起一个不同的BIO类,一个从BIO_s_accept返回基于BIO_METHOD的类,这个BIO类产生一个服务器接口能够接受远距离传输。在第50-51行,有一个对于6001端口的BIO_do_accept捆绑接口;接下来BIO_do_accept将锁定等待一个远距离连接。该循环持续到连接成功,当一个连接产生,对于连接到BIO接口的do_server_loop,产生一个新的连接线程,do_server_loop从接口简单的读取数据并写回stdout。如果在这里发生错误,该函数返回并且线程中止。比如,在33行ERR_remove_state为线程产生一个错误队列存储器。
例5-4.服务器应用
1 #include "common.h"
2
3 void do_server_loop(BIO *conn)
4 {
5 int err, nread;
6 char buf[80];
7
8 do
9 {
10 for (nread = 0; nread < sizeof(buf); nread += err)
11 {
12 err = BIO_read(conn, buf + nread, sizeof(buf) -
nread);
13 if (err <= 0)
14 break;
15 }
16 fwrite(buf, 1, nread, stdout);
17 }
18 while (err > 0);
19 }
20
21 void THREAD_CC server_thread(void *arg)
22 {
23 BIO *client = (BIO *)arg;
24
25 #ifndef WIN32
26 pthread_detach(pthread_self());
27 #endif
28 fprintf(stderr, "Connection opened.\n");
29 do_server_loop(client);
30 fprintf(stderr, "Connection closed.\n");
31
32 BIO_free(client);
33 ERR_remove_state(0);
34 #ifdef WIN32
35 _endthread();
36 #endif
37 }
38
39 int main(int argc, char *argv[])
40 {
41 BIO *acc, *client;
42 THREAD_TYPE tid;
43
44 init_OpenSSL();
45
46 acc = BIO_new_accept(PORT);
47 if (!acc)
48 int_error("Error creating server socket");
49
50 if (BIO_do_accept(acc) <= 0)
51 int_error("Error binding server socket");
52
53 for (;;)
54 {
55 if (BIO_do_accept(acc) <= 0)
56 int_error("Error accepting connection");
57
58 client = BIO_pop(acc);
59 THREAD_CREATE(tid, server_thread, client);
60 }
61
62 BIO_free(acc);
63 return 0;
64 }
现在我们了解了实例应用,我们将逐步学会使用SSL通信。
5.1.2 步骤一::SSL版本选择和证书准备
为了SSL的连接安全,我们必须为保证正确性选择安全的版本并提供准确的证明信息。由于这是我们对于 SSL API 的初步了解,我们将隐藏功能和结构的背景信息。