首页 使用_OpenSSL_API_进行安全编程

使用_OpenSSL_API_进行安全编程

举报
开通vip

使用_OpenSSL_API_进行安全编程使用 OpenSSL API 进行安全编程 创建基本的安全连接和非安全连接 Kenneth Ballard (kenneth.ballard@ptk.org), 自由程序员 Kenneth 是 Peru State College(位于 Peru, Nebraska)计算机科学专业的大四学生。他还是 学生报 The Peru State Times 的职业作者。他拥有 Southwestern Community College (位于 Creston, Iowa)计算机编程专业的理学副学士(Associate o...

使用_OpenSSL_API_进行安全编程
使用 OpenSSL API 进行安全编程 创建基本的安全连接和非安全连接 Kenneth Ballard (kenneth.ballard@ptk.org), 自由程序员 Kenneth 是 Peru State College(位于 Peru, Nebraska)计算机科学专业的大四学生。他还是 学生报 The Peru State Times 的职业作者。他拥有 Southwestern Community College (位于 Creston, Iowa)计算机编程专业的理学副学士(Associate of Science)学位, 在这所大学里,他是一名半工半读的 PC 技术员。他的研究领域包括 Java、C++、COBOL、 Visual Basic 和网络。 简介: 学习如何使用 OpenSSL —— 用于安全通信的最著名的开放库 —— 的 API 有些强人所难,因为其文档并不完全。您可以通过本文中的提示补充这方面的知识,并驾驭该 API。在建立基本的连接之后,就可以查看如何使用 OpenSSL 的 BIO 库来建立安全连接和非安全连接。与此同时,您还会学到一些关于错误检测的知识。 OpenSSL API 的文档有些含糊不清。因为还没有多少关于 OpenSSL 使用的教程,所以对初学者来说,在 应用程序中使用它可能会有一些困难。那么怎样才能使用 OpenSSL 实现一个基本的安全连接呢? 本教程将帮助您解决这个问题。 学习如何实现 OpenSSL 的困难部分在于其文档的不完全。不完全的 API 文档通常会妨碍开发人员 使用该 API,而这通常意味着它注定要失败。但 OpenSSL 仍然很活跃,而且正逐渐变得强大。这是为什么? OpenSSL 是用于安全通信的最著名的开放库。在 google 中搜索“SSL library”得到的返回结果中, 列表最上方就是 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 就不具备这一支持。 先决条件 要充分理解并利用本文,您应该: ​ 精通 C 编程。 ​ 熟悉 Internet 通信和支持 Internet 的应用程序的编写。 并不绝对 要求 对教师党员的评价套管和固井爆破片与爆破装置仓库管理基本要求三甲医院都需要复审吗 您熟悉 SSL ,因为稍后将给出对 SLL 的简短说明;不过,如果您希望得到详细论述 SSL 的文章的链接,请参阅 参考资料部分。拥有密码学方面的知识固然好,但这 并不是必需的。 什么是 SSL? SSL 是一个缩写,代表的是 Secure Sockets Layer。它是支持在 Internet 上进行安全通信的 标准,并且将数据密码术集成到了协议之中。数据在离开您的计算机之前就已经被加密,然后只有 到达它预定的目标后才被解密。证 关于书的成语关于读书的排比句社区图书漂流公约怎么写关于读书的小报汉书pdf 和密码学算法支持了这一切的运转,使用 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 发行版本(Red Hat、Mandrake 等),建议您通过从发行版本制造商那里获得 RPM 程序包来更新您的 OpenSSL 发行版本。出于安全方面的原因,建议您使用 最新版本的发行版本。如果您的发行版本不能使用最新版本的 OpenSSL,那么建议您只覆盖库文件,不要覆盖 可执行文件。OpenSSL 附带的 FAQ 文档中包含了有关这方面的细节。 还要注意的是,OpenSSL 并没有在所有的平台上都获得官方支持。虽然制造商已经尽力使其能够跨平台兼容, 但仍然存在 OpenSSL 不能用于您的计算机 和/或 操作系统的可能。请参阅 OpenSSL 的 Web 站点( 参考资料 中 的链接),以获得关于哪些平台可以得到支持的信息。 如果想使用 OpenSSL 来生成证书请求和数字证书,那么必须创建一个配置文件。在 OpenSSL 程序包 的 apps 文件夹中,有一个名为 openssl.cnf 的 可用模板文件。我不会对该文件进行讨论,因为这不在本文要求范围之内。不过,该模板文件有一些非常好的注释,而且如果 在 Internet 上搜索,您可以找到很多讨论修改该文件的教程。 头文件和初始化 本教程所使用的头文件只有三个:ssl.h、bio.h 和 err.h。它们都位于 openssl 子目录中,而且都是开发您的项目 所必需的。要初始化 OpenSSL 库,只需要三个代码行即可。清单 1 中列出了所有内容。其他的头文件 和/或 初始化函数可能 是其他一些功能所必需的。 清单 1. 必需的头文件 /* OpenSSL headers */ #include "openssl/bio.h" #include "openssl/ssl.h" #include "openssl/err.h" /* Initializing OpenSSL */ 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) { /* Handle the failure */ } if(BIO_do_connect(bio) <= 0) { /* Handle failed connection */ } 在这里,第一行代码使用指定的主机名和端口创建了一个新的 BIO 对象,并以所示风格对该对象进行 格式化。例如, 如果您要连接到 www.ibm.com 的 80 端口,那么该字符串将是 www.ibm.com:80 。调用 BIO_do_connect 检查连接是否成功。如果出错,则返回 0 或 -1。 与服务器进行通信 不管 BIO 对象是套接字还是文件,对其进行的读和写操作都是通过以下两个函数来完成的: BIO_read 和 BIO_write 。 很简单,对吧?精彩之处就在于它始终如此。 BIO_read 将尝试从服务器读取一定数目的字节。它返回读取的字节数、 0 或者 -1。在受阻塞的连接中,该函数返回 0,表示连接已经关闭,而 -1 则表示连接出现错误。在非阻塞连接的情况下,返回 0 表示没有可以获得的数据,返回 -1 表示连接出错。可以调用 BIO_should_retry 来确定是否可能重复出现该错误。 清单 4. 从连接读取 int x = BIO_read(bio, buf, len); if(x == 0) { /* Handle closed connection */ } else if(x < 0) { if(! BIO_should_retry(bio)) { /* Handle failed read here */ } /* Do something to handle the retry */ } BIO_write 会试着将字节写入套接字。它将返回实际写入的 字节数、0 或者 -1。同 BIO_read ,0 或 -1 不一定表示错误。 BIO_should_retry 是找出问题的途径。如果需要重试写操作,它必须 使用和前一次完全相同的参数。 清单 5. 写入到连接 if(BIO_write(bio, buf, len) <= 0) { if(! BIO_should_retry(bio)) { /* Handle failed write here */ } /* Do something to handle the retry */ } 关闭连接 关闭连接也很简单。您可以使用以下两种方式之一来关闭连接: BIO_reset 或 BIO_free_all 。如果您还需要重新使用对象,那么请使用第一种方式。 如果您不再重新使用它,则可以使用第二种方式。 BIO_reset 关闭连接并重新设置 BIO 对象的内部状态,以便可以重新使用连接。如果要在整个应用程序中使用同一对象,比如使用一台安全的聊天 客户机,那么这样做是有益的。该函数没有返回值。 BIO_free_all 所做正如其所言:它释放内部结构体,并释放 所有相关联的内存,其中包括关闭相关联的套接字。如果将 BIO 嵌入于一个类中,那么应该在类的 析构函数中使用这个调用。 清单 6. 关闭连接 /* To reuse the connection, use this line */ BIO_reset(bio); /* To free it from memory, use this line */ BIO_free_all(bio); 建立安全连接 现在需要给出建立安全连接需要做哪些事情。惟一要改变的地方就是建立并进行连接。其他所有内容都是相同的。 安全连接要求在连接建立后进行握手。在握手过程中,服务器向客户机发送一个证书, 然后,客户机根据一组可信任证书来核实该证书。它还将检查证书,以确保它没有过期。要 检验证书是可信任的,需要在连接建立之前提前加载一个可信任证书库。 只有在服务器发出请求时,客户机才会向服务器发送一个证书。该过程叫做客户机认证。使用证书, 在客户机和服务器之间传递密码参数,以建立安全连接。尽管握手是在建立连接之后才进行的,但是客户机或服务器可以在任何时刻请求进行一次新的握手。 参考资料 部分中列出的 Netscasp 文章 和 RFC 2246 ,对握手以及建立安全连接的其他方面的知识进行了更详尽的论述。 为安全连接进行设置 为安全连接进行设置要多几行代码。同时需要有另一个类型为 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)) { /* Handle failed load here */ } 如果打算使用目录存储可信任库,那么必须要以特定的方式命名文件。OpenSSL 文档清楚 地说明了应该如何去做,不过,OpenSSL 附带了一个名为 c_rehash 的工具, 它可以将文件夹配置为可用于 SSL_CTX_load_verify_locations 的 路径参数。 清单 9. 配置证书文件夹并使用它 /* Use this at the command line */ c_rehash /path/to/certfolder /* then call this from within the application */ if(! SSL_CTX_load_verify_locations(ctx, NULL, "/path/to/certfolder")) { /* Handle error here */ } 为了指定所有需要的验证证书,您可以根据需要命名任意数量的单独文件或文件夹。您还可以同时指定 文件和文件夹。 创建连接 将指向 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. 打开安全连接 /* Attempt to connect */ BIO_set_conn_hostname(bio, "hostname:port"); /* Verify the connection opened and perform the handshake */ if(BIO_do_connect(bio) <= 0) { /* Handle failed connection */ } 连接建立后,必须检查证书,以确定它是否有效。实际上,OpenSSL 为我们完成了这项任务。如果证书有致命的 问题(例如,哈希值无效),那么将无法建立连接。但是,如果证书的问题并不是致命的(当它已经过期 或者尚不合法时),那么仍可以继续使用连接。 可以将 SSL 结构作为惟一参数,调用 SSL_get_verify_result 来查 明证书是否通过了 OpenSSL 的检验。如果证书通过了包括信任检查在内的 OpenSSL 的内部检查,则返回 X509_V_OK。如果有地方出了问题,则返回一个错误代码,该代码被记录在命令行工具的 verify 选项下。 应该注意的是,验证失败并不意味着连接不能使用。是否应该使用连接取决于验证结果和安全方面的考虑。例如, 失败的信任验证可能只是意味着没有可信任的证书。连接仍然可用,只是需要从思想上提高安全意识。 清单 12. 检查证书是否有效 if(SSL_get_verify_result(ssl) != X509_V_OK) { /* Handle the failed verification */ } 这就是所需要的全部操作。通常,与服务器进行通信都要使用 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:[error code]:[library name]:[function name]:[reason string]:[file name]:[line]:[optional text message] 其中, [pid] 是进程 ID, [error code] 是一个 8 位十六进制代码, [file name] 是 OpenSSL 库中的源代码文件, [line] 是源文件中的行号。 清单 16. 转储错误队列 ERR_print_errors_fp(FILE *); ERR_print_errors(BIO *); 开始做吧 使用 OpenSSL 创建基本的连接并不困难,但是,当试着确定该如何去做时,文档可能是一个小障碍。本文向您介绍了一些基本概念,但 OpenSSL 还有很多灵活之处有待发掘,而且 您还可能需要一些高级设置,以便项目能够充分利用 SSL 的功能。 本文中有两个样例。一个样例展示了到 http://www.verisign.com/ 的非安全连接,另一个则展示了到 http://www.verisign.com/ 的安全 SSL 连接。两者都是连接到服务器并下载其主页。它们没有进行 任何安全检查,而且库中的所有设置都是默认值 —— 作为本文的一部分,应该只将这些用于教学目的。 在任何支持的平台上,源代码的编译都应该是非常容易的,不过我建议您使用最新版本的 OpenSSL。在撰写本文时,OpenSSL 的最新版本是 0.9.7d。 参考资料 ​ 您可以参阅本文在 developerWorks 全球站点上的 英文原文. ​ 下载本文中用到的 源代码。 ​ 您可以从 OpenSSL Project 下载 OpenSSL 源文件;一定要去查看一下 文档 的当前状态。您还可以从 邮件列表(滚动到底部,以获得到存档文件的链接)中学到很多知识,而且应该——当然,如往常一样——花一些时间去 阅读 FAQ! ​ OpenSSL 源自 SSLeay (它甚至有非常 完善的文档)。 ​ 此外,请参阅由两部分构成的文章“ An Introduction to OpenSSL Programming”( Linux Journal,2001 年) (以及 第二部分), 而且可以通过( informIT, 2001 年)获得的另一篇来自 Sams 的文章 “ Securing Sockets with OpenSSL”和它的 第二部分,该文章也是由两部分构成的。 ​ 在线阅读 BIO library documentation 和 Network Security with OpenSSL (O'Reilly & Associates,2002 年)的样例章节。 Linux Socket Programming (Sams,2001 年)摘自 Sams 的书。 ​ OpenSSL 的发布遵循 BSD/Apache-type 许可。如果您是自由软件(Free Software)的支持者(或者是 good documentation 的支持者), 您可能还希望查看 The GNU Transport Layer Security Library (注意,如果没有异常子句,GPL 的软件不能针对 OpenSSL 进行链接)。 Mozilla Network Security Services(NSS) 是双许可的,它既遵循 Mozilla Public License(MPL)又遵循 GNU General Public License (GNU GPL),而且有相当好的 文档。 要深入了解 TLS,请阅读 Wikipedia 的文章 Transport Layer Security。 ​ 可以在 RFC 2246 中找到关于 Transport Layer Security 的 备忘录和技术细节,RFC 2246 定义了标准,并且它被 RFC 3546 更新, 后者定义了对 TLS 协议的扩展。 ​ “ 使用 Twisted 框架进行网络编程, 第 4 部分”( developerWorks,2003 年 9 月)中 David Mertz 讨论了使用 Python twisted 框架进行 SSL 编程。 ​ 要深入学习套接字编程,请参阅 Linux Socket 编程,第一部分( developerWorks,2003 年 10 月)和 Linux Socket 编程,第二部分,这也是 David Mertz 的一个教程系列( developerWorks,2004 年 1 月)。 对那些刚开始进行套接字编程的人来说, Beej's Guide to Network Programming Using Internet Sockets 也是一个不错的参考资料。 ​ 如果您是 刚刚 开始接触套接字,那么请先阅读 “ Understanding Sockets in Unix, NT, and Java”( developerWorks,1998 年 6 月), 那篇文章提供了什么是套接字以及它们适用于何处的极好的入门级概述。 ​ 此外,还可以参阅来自 Communications Programming Concepts Sockets 的关于 Sockets 的 IBM 文档,以及来自 Technical Reference: Communications, Volume 2 的 Programming sockets on AIX。 ​ 可以通过“ Encryption using OpenSSL's crypto libraries” ( Linux Gazette,2003 年)初步了解加密,并通过 “ Introduction to Cryptography” ( PGP Corporation,2003 年 5 月 —— XPDF 格式) 或 “ Introduction to cryptography” ( developerWorks,2001 年 3 月)获得对加密的总体上的深入理解。 可以以 Postscript 和 PDF 格式在线获得 Handbook of Appplied Cryptography (CRC Press,1996 年) (可以通过订购获得更新后的 2001 版本)。 ​ 在 developerWorks Linux 专区 可以找到 更多为 Linux 开发人员准备的参考资料。 ​ 可以在 Developer Bookstore Linux 区中定购 打折出售的 Linux 书籍。 ​ 从 developerWorks 的 Speed-start your Linux app 专区下载可以运行于 Linux 之上的精选的 developerWorks Subscription 产品免费测试版本,其中包括 WebSphere Studio Site Developer、WebSphere SDK for Web services、WebSphere Application Server、DB2 Universal Database Personal Developers Edition、Tivoli Access Manager 和 Lotus Domino Server。想更快地开始上手,请参阅针对各个产品的 how-to 文章和技术支持。 使用 OpenSSL API 进行安全编程,第 2 部分: 安全握手 防止中间人(MITM)攻击 Kenneth Ballard (kenneth.ballard@ptk.org), 自由程序员 Kenneth Ballard 拥有 Peru State College(位于 Peru, Nebraska)的计算机科学专业学士学位,在这里,他是校报 The Peru State Times 的专职作者。他还拥有 Southwestern Community College(位于 Creston, Iowa)的计算机编程专业副学士学位,在这里,他是一名半工半读的 PC 技术员。他的研究领域包括 Java、C++、COBOL、 Visual Basic、网络、数据库管理和 Internet 编程。您可以通过 kenneth.ballard@ptk.org 与 Kenneth 联系。 简介: 安全套接字层(Secure Sockets Layer,SSL)会话中的安全握手非常重要,这是因为该连接中的所有安全性都是在握手过程中建立的。本文将介绍如何增强 SSL 握手的安全性,从而防止中间人(MITM)攻击 —— 此时入侵的一方会伪装成另外一个可信源。本文还会介绍数字证书的概念,以及 OpenSSL API 如何处理数字证书。 不久之前,安全握手是双方的业务得以实现的一个标记。毕竟,握手是一次面对面的机会,可以对潜在的合作者进行评价。安全且可信的握手意味着事务的双方都相信它们正在做的事情对双方都是有益的。不安全的握手标记着只有一方会对事务有着正确的理解。 握手的工作方式与在线事务相同。 developerWorks 上的前一篇文章“使用 OpenSSL API 进行安全编程,第 1 部分:API 概述” 向您介绍了如何使用 OpenSSL 创建基本、简单的安全连接。然而,这篇文章只是展示了基本的默认设置;它并没有介绍如何对内容进行定制。不过,我仍然建议大家阅读这篇文章,这样可以使您 对本文的理解更加完整,因为前一篇文章介绍了数字证书的概念,并且介绍了如何判断一个证书是否成功通过了 OpenSSL 的内部验证。 本文将深入介绍 OpenSSL,向您介绍如何增强握手的安全性,防止所谓的 中间人 (MITM)攻击。 关于数字证书 在本文后面,我们将介绍如何获取数字证书并对数字证书进行验证,因此下面我们将快速讨论一下什么是数字证书,以及它是如何工作的。如果您熟悉数据加密和 SSL 的知识,就可以跳过本节。要了解更多有关加密和 SSL 问题的信息,请参阅在本文后面 参考资料 中所列出的文章和教程。 数字证书的最简单形式就是 不对称加密密钥(asymmetric cryptography key)。目前关于数字证书的标准中都 有一些标识信息,在密钥中也都包含了这些信息。一个典型的数字证书包含所有者的名字(如果这个证书是在一个 Web 服务器上使用的,那么名字就是完整的域名)以及联系信息,还有一个有效日期范围,以及一个安全性签名,用来验证这个证书没有被篡改。 数字证书可以使用 OpenSSL 命令行工具或其他用于此目的的工具简单地创建。但是任何人创建的数字证书都有一个信任的问题。数字证书不仅仅是一个加密密钥,它还是一个在线凭证。证书会 向那些试图与您进行通信的人证明您的身份。为了显示信任关系,数字证书可以由认证权威(CA)机构进行签名。 认证权威在数字安全性领域充当一个可信的第三方。由于在在线领域中证明某个实体的身份非常困难,认证权威就接管了这个挑战。它们为那些购买证书或对证书进 行签名的用户的身份提供证明。因此,要信任一个证书,用户只需要信任证书权威即可。用户通过拥有并使用 CA 的信任证书来表明自己对认证权威的信任。Verisign 和 Thawte 是非常知名的认证权威。 如果一个证书的安全性曾经受到过威胁,那么这个证书就会被丢弃 —— 也就是说,将其声明为无效。当一个证书被声明为无效时,CA 不可能将其通知所有拥有该证书拷贝的人。相反,CA 会发布一个 证书撤销列表(Certificate Revocation List)(CRL)。浏览器和其他使用数字证书的程序都可以验证这个证书已经被其属主或 CA 撤销了。 证书的撤销也可以使用 OCSP 协议进行检查。OCSP 代表 Online Certificate Status Protocol(在线证书状态协议),它是在 RFC 2560 中定义的。OpenSSL 既有 OCSP 的功能,又有 CRL 的功能,但是对这些功能的介绍已经超出了本文的范围。目前数字证书所采用的标准是 X.509,这是在 RFC 3280 中定义的。 开展业务之前的握手 由于本文重点要介绍在握手过程中服务器数字证书的处理,因此让我们来深入介绍一下握手是如何工作的。如果您熟悉 SSL 的过程,可以跳过本节。 在一个连接上开始握手通常是从客户机向服务器说“Hello”开始的。helllo 消息(在规范中就是这么说的)包含了客户机的安全性参数: ​ SSL 版本号 ​ 随机生成数据 ​ 密码设置 ​ 通信所需要的其他内容 服务器会使用自己的 hello 消息进行响应,其中包含了服务器的安全性参数,这些信息与客户机所提供的信息的类型相同。此时服务器还会发送自己的数字证书。如果客户机还要为这个连接进行认证,那么服务器还会发送一个请求,索取客户机的证书。 当客户机接收到服务器端的 hello 消息之后,数字证书就要进行验证了。这包括检查证书的各种参数,从而确保该证书从未受到过侵害,同时是在它的有效期内使用的。 此处还要执行的另外一个步骤是根据连接所使用的主机名对证书的名字进行检查。虽然这不是 SSL 标准的一部分,但是这个步骤却是高度建议的,它可以防止中间人攻击。这个步骤会验证证书就是来自您认为它所来自的那个实体。如果这两个实体在此处不能匹 配,那么就只能怀疑这个证书是无效的。 在客户机和服务器之间共享的随机数据用来创建 premaster secret,这是一个只有服务器和客户机才会知道的共享秘密值,并且只用于这个会话。这个秘密值会对服务器的数字证书进行加密,并发送给服务器用于验证客户机的身份。 如果服务器请求客户机认证,那么客户机就会对这个在握手过程中随机生成的数据(只有服务器和客户机知道它)创建一个单向的 hash 值。客户机使用自己的私钥对这个 hash 值进行签名,并将签名后的数据和数字证书发送给服务器。服务器使用这些信息对客户机进行认证。 如果认证成功,那么服务器和客户机就会通过一个算法使用这个共享的随机数据来创建 master secret。从 master secret 中,客户机和服务器可以创建 session keys(会话密钥),这是选择用来对会话数据进行加密所使用的对称密码中的对称密钥。 客户机通过向服务器发送一个表明自己已经完成握手的消息,以及一组加密的单向 hash 值让服务器进行验证,从而结束握手的过程。服务器也会向客户机发送一个类似的消息。客户机和服务器在结束握手并开始通信之前,都要对这些数据进行验证。 中间人 虽然这是孩子们玩的一个游戏,但却也是在公钥基础设施(PKI)上可能发生的一种很严重的攻击。当我们在讨论有关数字证书的问题时,就必须考虑中间人攻击的问题,因为不管 SSL 连接之后的安全参数如何,中间人攻击都可以让这些防范措施形同虚设。 假设 Casey 和 Samantha 希望使用 SSL 进行通信。Isabel 是一个第三方,她截获了这个连接请求,并在他们两个之前充当代理。当她注意到正在建立一个 SSL 连接时,就向 Samantha 伪装成 Casey,向 Casey 伪装成 Samantha。由于她位于中间,可以截获会话双方的内容。如果这个会话中包含帐号和个人信息,那么她就可以使用这些信息窃取他人的身份了。 在这种情况中,信任链和证书中的通用名可以防止中间人攻击的发生。在握手过程中,会对证书进行交换。在分析证书的有效性时,同时还会检查可信签名。如果服务器证书中的通用名是与证书的其余部分一起验证的,那么这种攻击就不攻自破了,对么?其实不完全对。 让我们假设 Isabel 有一个证书,其中有 Samantha 的名字;并且这个证书由 Casey 信任模型中的一个 CA 进行了签名。此时只通过检查通用名并不能避免 MITM 攻击的发生。证书及其信任关系都是有效的,名字也检查出来了。我们现在有了一个大麻烦。 然而,如果考虑一下认证权威,这个问题就并不重要了。大部分认证权威都会在发行带有个人名的数字证书之前就试图验证他的身份就是本人。由于这个原因,Isabel 就很难从一个知名的认证权威那里获得一个带有 Samantha 名字的数字证书。 如果在 CA 中工作,则可以消弱这种安全设施的作用。例如,如果 CA 和 Isabel 都在一个公司工作(换而言之,是一个“内部工作”)。那么用来签名的密钥就有可能会被 CA 内部工作的人员窃取,他们随后就可以使用任何人的名字来创建任意的证书。尽管在创建签名时要使用证书的私有部分,但是采用一些工程的方法或类似的技术也可 以窃取密码。 MITM 攻击在使用 代理服务器 的情况中尤其重要。因为安全连接必须由代理服务器提供一个隧道才能到达目的地,因此恶意的代理服务器就可以很容易地窃取任何会话。恶意的代理可以在并不存 在隧道时却伪装成仿佛隧道真正存在一样。在使用 Internet 上的“匿名代理”时,记得这一点尤其重要:您正在通过它们的系统来发送用户名和密码,怎样才能相信它们呢? 然而我们要认识到,这种攻击并不仅仅存在于计算机和数字安全领域。有一个女人曾经使用类似于 MITM 攻击的技术抢劫了很多家的很多钱(请参阅 参考资料)。 OpenSSL 和数字证书 OpenSSL 有一个专门用于数字证书的库。假设您现在已经有了 OpenSSL 的源代码,那么这个库的源代码就位于 crypto/x509 和 crypto/x509v3 目录中。源代码为数字证书的处理定义了几个结构。表 1 中列出了这些结构。 表 1. 与 X.509 证书有关的 OpenSSL 结构 结构 功能 X509 包含所有有关数字证书的数据 X509_ALGOR 提供该证书设计所针对的算法 X509_VAL 该证书有效的时间跨度 X509_PUBKEY 证书的公钥算法,通常是 RSA 或 DSA X509_SIG 证书的 hash 签名 X509_NAME_ENTRY 证书所包含的数据的各个项 X509_NAME 包含名字项的堆栈 这些只是其中涉及的几个结构。在 OpenSSL 中使用的大部分 X.509 结构您自己在应用程序中几乎都不会用到。在上面列出的这些结构中,本文中只使用了两个:X509 和 X509_NAME。 在这些结构之上,有一些用来处理数字证书的函数。这些函数得名于它们所适用的结构。例如,一个名字以 X509_NAME 开始的函数,通常会应用于一个 X509_NAME 结构。后面我们会根据需要介绍一些函数。 提供自己的信任证书 在数字证书进行信任验证之前,必须为在为安全连接设置时创建的 OpenSSL SSL_CTX 对象提供一个默认的信任证书,这可以使用几种方法来提供,但是最简单的方法是将这个证书保存为一个 PEM 文件,并使用 SSL_CTX_load_verify_locations(ctx, file, path); 将其加载到 OpenSSL 中。file 是包含一个或多个 PEM 格式的证书的文件的路径。path 是到一个或多个 PEM 格式文件的路径,不过文件名必须使用特定的格式。将信任证书保存在一个 PEM 文件中比较简单,这样可以使 path 参数为空,如下所示: SSL_CTX_load_verify_locations(ctx, "/path/to/trusted.pem", NULL);。 尽管当信任证书在一个目录中有多个单独的文件时更容易添加或更新,但是您不太可能会如此频繁地更新信任证书,因此不必担心这个问题。 验证证书 在通信继续连接或接收证书之前,请使用 SSL_get_verify_result() 来确定 OpenSSL 内部对证书的验证结果。如果 SSL_get_verify_result() 返回的代码不是 X509_V_OK,那么这就意味着这个证书无效吗?这要取决于返回代码。 通常,如果返回代码不是 X509_V_OK,那么这个证书就有问题,或者这个证书存在安全性隐患。时刻要记住一件事情:OpenSSL 在对证书进行验证时,有一些安全性检查并没有执行,包括证书的失效检查和对证书中通用名的有效性验证。 SSL_get_verify_result() 所返回的代码在 OpenSSL 文档的 verify 部分中都进行了介绍,这是在 apps 之下列出的。有些代码的说明是尚未使用,意味着它们永远不会返回。有些代码非常重要,而有些则不太重要。例如,如果由于没有加载所保存的信任证书,而不能对信任证书进行验证,那么是否继续进行通信,就完全取决于开发者了。 不管验证结果如何,是否继续使用一些可能不安全的参数也完全取决于开发者。由于证书可能是不安全的,因此会返回错误代码。 检索证书 如果您希望向用户显示证书的内容,或者要根据主机名或证书权威对证书进行验证,那么就需要检索证书的内容。要在验证测试结果之后再检索证书,请调用 SSL_get_peer_certificate()。它返回一个指向该证书的 X509 指针,如果证书不存在,就返回 NULL(参见清单 1)。 清单 1. 检索证书 X509 * peerCertificate; if(SSL_get_verify_result(ssl) == X509_V_OK) peerCertificate = SSL_get_peer_certificate(ssl); else { /* Handle verification error here */ } 验证证书 在握手时所提供的服务器的证书应该有一个名字与该服务器的主机名匹配。如果没有,那么这个证书就应该标记为值得怀疑的。内部验证过程已经对证书进行信任和 有效期的验证;如果这个证书已经超期,或者包含一个不可信的签名,那么这个证书就会被标记为无效的。由于这不是 SSL 标准的一部分,因此 OpenSSL 并不需要根据主机名对该证书的名字进行检查。 证书的“名字”实际上是证书中的 Common Name 字段。这个字段应该从证书中进行检索,并根据主机名进行验证。如果二者不能匹配,就只有怀疑这个证书无效了。有些公司(例如 Yahoo)就在不同的主机上使用相同的证书,即使证书中的 Common Name 只是用于一个主机的。为了确保这个证书是来自于相同的公司,可以进行更深入的检查,但是这完全取决于项目的安全性需要。 从证书中检索通用名需要两个步骤: ​ 从证书结构检索 X509_NAME 对象。 ​ 然后从 X509_NAME 对象检索名字。 使用 X509_get_subject_name() 从证书中检索 X509_NAME 结构。这会返回一个指向 X509_NAME 的对象。从现在开始,请使用 X509_NAME_get_text_by_NID() 来检索通用名,并保存到一个字符串中(如清单 2 所示)。 清单 2. 检索并验证 Common Name char commonName [512]; X509_NAME * name = X509_get_subject_name(peerCertificate); X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512); /* More in-depth checks of the common name can be used if necessary */ if(stricmp(commonName, hostname) != 0) { /* Handle a suspect certificate here */ } 使用标准的 C 字符串函数或您习惯使用的字符串库对通用名和主机名进行比较。对不匹配的处理,完全取决于项目的需要或用户的决策。如果要更深入地进行检查,我建议使用一个单独的字符串库来降低复杂性。 获得信任 在本文中,我们已经介绍了如何增强握手的安全性,从而防止中间人攻击(攻击一方伪装成另外一个可信源),我们还介绍了数字证书的概念,以及 OpenSSL API 如何处理数字证书。 记住,在 SSL 会话过程中增强会话的安全性非常重要,这是因为该连接中的所有安全性都是在握手过程中建立的。遵循本文中概要介绍的每个步骤到底有多重要取决于项目的要求以及开发者的决定。 参考资料 ​ 您可以参阅本文在 developerWorks 全球站点上的 英文原文。 ​ 从作者的 Web 站点上下载 源代码。 ​ “使用 OpenSSL API 进行安全编程:API 概述”(developerWorks,2004 年 7 月)展示了如何使用 OpenSSL API 建立一个简单的连接,这是本文的一个很好的预读物。 ​ Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile 是有关 X.509 标准的很好资源。 ​ Bruce Schneier 所编写的 Secrets and Lies: Digital Security in a Networked World (Wiley Publishing, Inc.,2004
本文档为【使用_OpenSSL_API_进行安全编程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_462183
暂无简介~
格式:doc
大小:190KB
软件:Word
页数:29
分类:互联网
上传时间:2011-07-14
浏览量:25