分类

  • 软件天地

  • 使用 OpenSSL API 进行安全编程‖
    难,因为其文档并不完全。
    立基本的连接之后,就可以
    与此同时,您还会学到一些
    —— 用于安全通信的最著名的
    您可以通过本文中的提示补充这
    查看如何使用 OpenSSL 的 BIO
    关于错误检测的知识。
    开放库 —— 的 API 有些强人所
    方面的知识,并驾驭该 API。在建
    库来建立安全连接和非安全连接。

      OpenSSL API 的文档有
    初学者来说,在应用程序中
    基本的安全连接呢?本教程
    些含糊不清。因为还没有多少关
    使用它可能会有一些困难。那么
    将帮助您解决这个问题。
    于 OpenSSL 使用的教程,所以对
    怎样才能使用 OpenSSL 实现一个


      学习如何实现 OpenSSL
    碍开发人员使用该 API,而
    渐变得强大。这是为什么?
    的困难部分在于其文档的不完
    这通常意味着它注定要失败。但

    全。不完全的 API 文档通常会妨
    OpenSSL 仍然很活跃,而且正逐


      OpenSSL 是用于安全通信的最著名的
    返回结果中,列表最上方就是 OpenSSL。
    Hudson 开发的 SSLeay 库。其他 SSL 工
    GNU TLS,以及 Mozilla Network Secur
    ,以获得其他信息)。
    开放库。在 google 中搜索“SSL library”得到的
    它诞生于 1998 年,源自 Eric Young 和 Tim
    具包包括遵循 GNU General Public License 发行的
    ity Services(NSS)(请参阅本文后面的 参考资料


      那么,是什么使得 OpenSSL 比 GNU
    是一方面因素(请参阅 参考资料)。此
    v3.0 协议,仅此而已。
    TLS、Mozilla NSS 或其他所有的库都优越呢?许可
    外,GNS TLS(迄今为止)只支持 TLS v1.0 和 SSL


      Mozilla NSS 的发行既
    进行选择。不过,Mozilla
    OpenSSL 是完全自包含的。
    NSS 获得了 PKCS #11 支持
    这一支持。
    遵循 Mozilla Public License
    NSS 比 OpenSSL 大,并且需要
    与 OpenSSL 相同,大部分 NSS
    ,该支持可以用于诸如智能卡这

    又遵循 GNU GPL,它允许开发人员
    其他外部库来对库进行编译,而
    API 也没有文档资料。Mozilla
    样的加密标志。OpenSSL 就不具备


      先决条件

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

      精通 C 编程

      熟悉 Internet 通信和支持 Internet 的应用程序的编写。     
      并不绝对要求您熟悉 SSL ,因为稍
    到详细论述 SSL 的文章的链接,请参阅
    并不是必需的。
    后将给出对 SLL 的简短说明;不过,如果您希望得
    参考资料部分S涤忻苈胙Х矫娴闹豆倘缓茫?br>

      什么是 SSL?

      SSL 是一个缩写,代表
    通信的标准,并且将数据密
    ,然后只有到达它预定的目
    OpenSSL,您将有机会切身
    的是 Secure Sockets Layer。
    码术集成到了协议之中。数据在
    标后才被解密。证书和密码学算
    体会它们。
    它是支持在 Internet 上进行安全
    离开您的计算机之前就已经被加密
    法支持了这一切的运转,使用


      理论上,如果加密的数
    过,由于计算机的变化一年
    加密协议被破解的可能性也
    据在到达目标之前被截取或窃听
    比一年快,而且密码翻译方法有
    在增大。
    ,那些菔遣豢赡鼙黄平獾摹2?br>了新的发展,因此,SSL 中使用的


      可以将 SSL 和安全连
    FTP。还可以用 SSL 来保护
    类连接都使用 SSL。如果连
    接用于 Internet 上任何类型的
    Telnet 会话。虽然可以用 SSL
    接传输敏感信息,则应使用 SSL
    协议,不管是 HTTP、POP3,还是
    保护任何连接,但是不必对每一


      什么是 OpenSSL?

      OpenSSL 不仅仅是 SSL。它可以实现
    和随机数字。关于 OpenSSL 库的内容非
    消息摘要、文件的加密和解密、数字证书、数字签名
    常多,远不是一篇文章可以容纳的。

      OpenSSL 不只是 API,它还是一个命
    ,而且更进一步,可以测试 SSL 服务器
    个认识。要获得关于如何使用 OpenSSL
    令行工具。命令行工具可以完成与 API 同样的工作
    和客户机。它还让开发人员对 OpenSSL 的能力有一
    命令行工具的资料,请参阅 参考资料 部分。

      您需要什么

      首先需要的是最新版本
    以自己编译的源代码,或者
    过,为了安全起见,我建议
    不是由 OpenSSL 的开发人
    的 OpenSSL。查阅参考资料部分
    最新版本的二进制文件(如果您
    您下载最新的源代码并自己编译
    员来编译和发行的。
    ,以确定从哪里可以获得最新的可
    不希望花费时间来编译的话)。不
    它。二进制版本通常是由第三方而


      一些 Linux 的发行版
    库来说,这足够了;不过,
    保持该版本一直是最新的。
    本附带了 OpenSSL 的二进制版
    如果您打算去做一些实际的事情

    本,对于学习如何使用 OpenSSL
    ,那么一定要得到最新的版本,并


      对于以 RPM 形式安装的 Linux 发行
    版本制造商那里获得 RPM 程序包来更新
    议您使用最新版本的发行版本。如果您的
    只覆盖库文件,不要覆盖可执行文件。Op

    版本(Red Hat、Mandrake 等),建议您通过从发行
    您的 OpenSSL 发行版本。出于安全方面的原因,建
    发行版本不能使用最新版本的 OpenSSL,那么建议您
    enSSL 附带的 FAQ 文档中包含了有关这方面的细节


      还要注意的是,OpenSS
    其能够跨平台兼容,匀?br>参阅 OpenSSL 的 Web 站点
    息。
    L 并没有在所有的平台上都获得
    存在 OpenSSL 不能用于您的计
    (参考资料 中的链接),以获

    官方支持。虽然制造商已经尽力使
    算机 和/或 操作系统的可能。请
    得关于哪些平台可以得到支持的信


      如果想使用 OpenSSL
    OpenSSL 程序包的 apps 文
    文件进行讨论,因为这不在
    如果在 Internet 上搜索,
    来生成证书请求和数字证书,那
    件夹中,有一个名为 openssl.c
    本文要求范围之内。不过,该模
    您可以找到很多讨论修改该文件
    么必须创建一个配置文件。在
    nf 的可用模板文件。我不会对该
    板文件有一些非常好的注释,而且
    的教程。

      头文件和初始化

      本教程所使用的头文件只有三个:ss
    中,而且都是开发您的项目所必需的。要
    1 中列出了所有内容。其他的头文件 和/
    l.h、bio.h 和 err.h。它们都位于 openssl 子目录
    初始化 OpenSSL 库,只需要三个代码行即可。清单
    或 初始化函数可能是其他一些功能所必需的。

      清单 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();                    

      建立非安全连接

      不管连接是安全的还是
    文件和套接字在内的各种类
    UU 或 Base64 编码的过滤
    不安全的,OpenSSL 都使用了一
    型的通信。您还可以将 OpenSSL
    器。
    个名为 BIO 的抽象库来处理包括
    设置成为一个过滤器,比如用于


      在这里对 BIO 库进行全面说明有点
    我将向您展示如何建立一个标准的套接字
    码行更少一些。
    麻烦,所以我将根据需要一点一点地介绍它。首先,
    连接。相对于使用 BSD 套接字库,该操作需要的代


      在建立连接(无论安全
    C 中为文件流创建 FILE 指
    与否)之前,要创建一个指向 B
    针。
    IO 对象的指针。这类似于在标准


      清单 2. 指针                                                              

      BIO * bio;                                                          

      打开连接

      创建新的连接需要调用
    号。也可以将其拆分为两个
    调用,另一个是设置端口号
    BIO_new_connect。您可以在同
    单独的调用:一个是创建连接并
    的 BIO_set_conn_port(或者 B
    一个调用中同时指定主机名和端口
    设置主机名的 BIO_new_connect
    IO_set_conn_int_port)调用。

      不管怎样,一旦 BIO 的主机名和端
    以影响它。如果创建 BIO 对象时遇到问
    行 BIO_do_connect 调用。
    口号都已指定,该指针会尝试打开连接。没有什么可
    题,指针将会是 NULL。为了确保连接成功,必须执


      清单 3. 创建并打开连接                                                    

      bio = BIO_new_connect("hostname:port");
      if(bio == NULL)                                                
      {                                                                            
        /* Handle the failure */                          
      }                                                                            

      if(BIO_do_connect(bio) < = 0)              
      {                                                                            
        /* Handle failed connection */              
      }                                                                            

      在这里,第一行代码使
    对该对象进行 格式化。例
    是 www.ibm.com:80。调用

    用指定的主机名和端口创建了一
    如,如果您要连接到 www.ibm.c
    BIO_do_connect 检查连接是否

    个新的 BIO 对象,并以所示风格
    om 的 80 端口,那么该字符串将
    成功。如果出错,则返回 0 或 -1


      与服务器进行通信

      不管 BIO 对象是套接字还是文件,
    的:BIO_read 和 BIO_write。很简单,
    对其进行的读和床僮鞫际峭ü韵铝礁龊赐瓿?br>对吧?精彩之处就在于它始终如此。

      BIO_read 将尝试从服务器读取一定
    受阻塞的连接中,该函数返回 0,表示连
    连接的情况下,返回 0 表示没有可以获
    BIO_should_retry 来确定是否可能重复
    数目的字节。它返回读取的字节数、 0 或者 -1。在
    接已经关闭,而 -1 则表示连接出现错误。在非阻塞
    得的数据,返回 -1 表示连接出错。可以调用
    出现该错误。

      清单 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 会试着将字
    BIO_read,0 或 -1 不一定
    写操作,它必须使用和前一
    节写入套接字。它将返回实际写
    表示错误。 BIO_should_retry
    次完全相同的参数。
    入的字节数、0 或者 -1。同
    是找出问题的途径。如果需要重试


      清单 5. 写入到连接                                                        

      if(BIO_write(bio, buf, len) < = 0)    
      {                                                                            
        if(! BIO_should_retry(bio))                    
        {                                                                        
          /* Handle failed write here */          
        }                                                                        

        /* Do something to handle th
    e retry */
      }                                                                            

      关闭连接

      关闭连接也很简单。您可以使用以下
    BIO_free_all。如果您还需要重新使用对
    ,则可以使用第二种方式。
    两种方式之一来关闭连接:BIO_reset 或
    象,那么请使用第一种方式。如果您不再重新使用它


      BIO_reset 关闭连接并
    在整个应用程序中使用同一
    函数没有返回值。
    重新设置 BIO 对象的内部状态
    对象,比如使用一台安全的聊天

    ,以便可以重新使用连接。如果要
    客户机,那么这样做是有益的。该


      BIO_free_all 所做正
    括关闭相关联的套接字。如
    调用。
    如其所言:它释放内部结构体,
    果将 BIO 嵌入于一个类中,那

    并释放所有相关联的内存,其中包
    么应该在类的析构函数中使用这个


      清单 6. 关闭连接                                                          


      /* To reuse the conn
    ection, use this line */

      BIO_reset(bio);                                                


      /* To free it from m
    emory, use this line */

      BIO_free_all(bio);                                          

      建立安全连接

      现在需要给出建立安全
    他所有内容都是相同的。
    连接需要做哪些事情。惟一要改

    变的地方就是建立并进行连接。其


      安全连接要求在连接建立后进行握手
    后,客户机根据一组可信任证书来核实该
    证书是可信任的,需要在连接建立之前提
    。在握手过程中,服务器向客户机发送一个证书,然
    证书。它还将检查证书,以确保它没有过期。要检验
    前加载一个可信任证书库。

      只有在服务器发出请求
    使用证书,在客户机和服务
    后才进行的,但是客户机或
    时,客户机才会向服务器发送一
    器之间传递密码参数,以建立安
    服务器可以在任何时刻请求进行
    个证书。该过程叫做客户机认证。
    全连接。尽管握手是在建立连接之
    一次新的握手。

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


      为安全连接进行设置

      为安全连接进行设置要多几行代码。
    保存了一些 SSL 信息。您也可以利用它
    法函数调用 SSL_CTX_new 来创建这个结
    同时需要有另一个类型为 SSL_CTX 的指针。该结构
    通过 BIO 库建立 SSL 连接。可以通过使用 SSL 方
    构,该方法函数通常是 SSLv23_client_method。

      还需要另一个 SSL 类
    所必需的)。以后还可以用
    型的指针来保持 SSL 连接结构
    该 SSL 指针来检查连接信息或
    (这是短时间就能完成的一些连接
    设置其他 SSL 参数。

      清单 7. 设置 SSL 指针                                                

      SSL_CTX * ctx = SSL_CTX_new(SSLv
    23_client_method());
      SSL * ssl;                                                          

      加载可信任证书库

      在创建上下文结构之后
    如果不能确认证书是可信任
    ,必须加载一个可信任证书库。
    的,那么 OpenSSL 会将证书标
    这是成功验证每个证书所必需的。
    记为无效(但连接仍可以继续)。

      OpenSSL 附带了一组可
    都是一个独立的文件 ——
    个存放过期证书的子目录。
    信任证书。它们位于源文件树的
    也就是说,需要单独加载每一个
    试图加载这些证书将会出错。
    certs 目录中。不过,每个证书
    证书。在 certs 目录下,还有一


      如果您愿意,可以分别加载每一个文
    可信任证书通常存放在源代码档案文件中
    文件中。如果已经有了一个可信任证书库
    文件替换清单 8 中的“TrustStore.pem

    件,但为了简便起见,最新的 OpenSSL 发行版本的
    ,这些档案文件位于名为“TrustStore.pem”的单个
    ,并打算将它用于特定的项目中,那么只需使用您的
    ”(或者使用单独的函数调用将它们全部加载)即可


      可以调用 SSL_CTX_loa
    参数:上下文指针、可信任
    信任库募蛑な榈哪柯肌?br>
    d_verify_locations 来加载可
    库文件的路径 和文件名,以及
    如果指定成功,则返回 1,如果
    信任证书库文件。这里要用到三个
    证书所在目录的路径。必须指定可
    遇到问题,则返回 0。

      清单 8. 加载信任库                                                        

      if(! SSL_CTX_load_verify_locatio
    ns(ctx, "/path/to/TrustStore.pem", NULL))
      {                                                                            
        /* Handle failed load here */                
      }                                                                            

      如果打算使用目录存储可信任库,那
    地说明了应该如何去做,不过,OpenSSL
    配置为可用于 SSL_CTX_load_verify_loc
    么必须要以特定的方式命名文件。OpenSSL 文档清楚
    附带了一个名为 c_rehash 的工具,它可以将文件夹
    ations 的路径参数。

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

      /* Use this at the command line */          

      c_rehash /path/to/certfolder                      


      /* then call this fr
    om within the application */

      if(! SSL_CTX_load_verify_locatio
    ns(ctx, NULL, "/path/to/certfolder"))
      {                                                                            
        /* Handle error here */                            
      }                                                                            

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


      创建连接

      将指向 SSL 上下文的指针作为惟一
    还需要获得指向 SSL 结构的指针。在本
    函数是用来设置 SSL_MODE_AUTO_RETRY
    望进行一次新的握手,那么 OpenSSL 可
    进行一次新的握手时,进行读或写操作都
    标记。
    参数,使用 BIO_new_ssl_connect 创建 BIO 对象。
    文中,只将该指针用于 SSL_set_mode 函数。而这个
    标记的。使用这个选项进行设置,如果服务器突然希
    以在后台处理它。如果没有这个选项,当服务器希望
    将返回一个错误,同时还会在该过程中设置 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, "host
    name:port");

      /* Verify the connec
    tion opened and perform the
    handshake */

      if(BIO_do_connect(bio) < = 0)              
      {                                                                            
        /* Handle failed connection */              
      }                                                                            

      连接建立后,必须检查
    务。如果证书有致命的问题
    问题并不是致命的(当它已
    证书,以确定它是否有效。实际
    (例如,哈希值无效),那么将
    经过期或者尚不合法时),那么
    上,OpenSSL 为我们完成了这项任
    无法建立连接。但是,如果证书的
    仍可以继续使用印?br>

      可以将 SSL 结构作为惟一参数,调
    OpenSSL 的检验。如果证书通过了包括信
    X509_V_OK。如果有地方出了问题,则返
    verify 选项下。
    用 SSL_get_verify_result 来查明证书是否通过了
    任检查在内的 OpenSSL 的内部检查,则返回
    回一个错误代码,该代码被记录在命令行工具的


      应该注意的是,验证失败并不意味着
    安全方面的考虑。例如,失败的信任验证
    只是需要从思想上提高安全意识。
    连接不能使用。是否应该使用连接取决于验证结果和
    可能只是意味着没有可信任的证书。连接仍然可用,


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

      if(SSL_get_verify_result(ssl) !=
    X509_V_OK)
      {                                                                            
        /* Handle the failed verification */  
      }                                                                            

      这就是所需要的全部操作。通常,与
    并且只需调用 BIO_free_all 或 BIO_res
    是否重用 BIO。
    服务器进行通信都要使用 BIO_read 和 BIO_write。
    et ,就可以关闭连接,具体调用哪一个方法取决于


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

    结构。可以调用 SSL_CTX_free 来


      清单 13. 清除 SSL 上下文                                            

      SSL_CTX_free(ctx);                                          

      错误检测

      显然 OpenSSL 抛出了某种类型的错
    ;ERR_get_error 可以完成这项任务;然
    向由 SSL_load_error_strings 或 ERR_l
    。可以在一个嵌套调用中完成这项操作。
    误。这意味着什么?首先,您需要得到错误代码本身
    后,需要将错误代码转换为错误字符串,它是一个指
    oad_BIO_strings 加载到内存中的永久字符串的指针


      表 1 略述了从错误栈检索错误的方
    个错误信息。
    法。清单 24 展示了如何打印文本字符串中的最后一


      表 1. 从栈中检索错误                                                      
      < center>[[The No.1 Picture.]]< /center>

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

      ERR_lib_error_string 指出错误发生在哪个库中             
      ERR_func_error_string 返回导致错
    误的 OpenSSL 函数

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

      printf("Error: %sn"
    , ERR_reason_error_string(ER
    R_get_error()));

      您还可以让库给出预先格交说拇?br>符串。该函数将错误代码和一个预分配的
    。如果参数为 NULL,则 OpenSSL 会将字
    并返回指向该缓冲区的指针。否则,它将
    ,那么在下一次调用 ERR_error_string
    误字符串。可以调用 ERR_error_string 来得到该字
    缓冲区作为参数。而这个缓冲区必须是 256 字节长
    符串写入到一个长度为 256 字节的静态缓冲区中,
    返回您给出的指针。如果您选择的是静态缓冲区选项
    时,该缓冲区会被覆盖。

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

      printf("%sn", ERR_error_string(
    ERR_get_error(), NULL));

      您还可以将整个错误队
    ERR_print_errors_fp 来实
    到 BIO,第二个函数将队列
    列转储到文件或 BIO 中。可以
    现这项操作。队列是以可读格式
    发送到 FILE。字符串格式如下
    通过 ERR_print_errors 或
    被转储的。第一个函数将队列发送
    (引自 OpenSSL 文档):

      [pid]:error:[error code]:[librar
    name]:[line]:[optional text message]
    y name]:[function name]:[reason string]:[file


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

      清单 16. 转储错误队列                                                    

      ERR_print_errors_fp(FILE *);                      
      ERR_print_errors(BIO *);                              

      使用 OpenSSL 创建基
    一个小障碍。本文向您介绍
    您还可能需要一些高级设置
    本的连接并不困难,但是,当试
    了一些基本概念,但 OpenSSL
    ,以便项目能够充分利用 SSL
    着确定该如何去做时,文档可能是
    还有很多灵活之处有待发掘,而且
    的功能。

      本文中有两个样例。一个样例展示了
    个则展示了到 http://www.verisign.com
    其主页。它们没有进行任何安全检查,而
    分,应该只将这些用于教学目的。
    到 http://www.verisign.com/ 的非安全连接,另一
    / 的安全 SSL 连接。两者都是连接到服务器并下载
    且库中的所有设置都是默认值 —— 作为本文的一部


    上一页 下一页




    map