基于 OpenSSL 的简单 C/S 通信 C 程序设计使用函数文档

Posted yogile

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 OpenSSL 的简单 C/S 通信 C 程序设计使用函数文档相关的知识,希望对你有一定的参考价值。

前言

  • [译自 openssl.org 官方英文文档 ]
  • 注意:本文只提供相关函数的说明、定义、参数、返回值、注释等的简单翻译,对于更多信息、疑问或错误之处,请阅读原英文文档。

函数

SSL_library_init()

  • 名称

    SSL_library_init,OpenSSL_add_ssl_algorithms,SSLeay_add_ssl_algorithms - 通过注册算法初始化SSL库
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_library_init(void);
    #define OpenSSL_add_ssl_algorithms()    SSL_library_init()
    #define SSLeay_add_ssl_algorithms()     SSL_library_init()
    
  • 描述

    SSL_library_init()注册可用的SSL / TLS密码和摘要。

    OpenSSL_add_ssl_algorithms()和SSLeay_add_ssl_algorithms()是同义词SSL_library_init() 。

  • 备注

    必须先调用SSL_library_init(),然后才能执行其他任何操作。SSL_library_init()不可重入。

  • 警告

    SSL_library_init()添加SSL / TLS直接和间接使用的密码和摘要。
    
  • 例子

    典型的TLS/SSL应用程序将从库初始化开始,并提供可读的错误消息。
    
        SSL_load_error_strings(); / *可读错误消息* / 
        SSL_library_init(); / *初始化库* /
    
  • 返回值

    SSL_library_init()始终返回“ 1”,因此可以安全地丢弃返回值。
    

SSL_CTX_new()

  • 名称

    SSL_CTX_new,SSLv23_method,SSLv23_server_method,SSLv23_client_method,TLSv1_2_method,TLSv1_2_server_method,TLSv1_2_client_method,TLSv1_1_method,TLSv1_1_server_method,TLSv1_1_client_method,TLSv1_method,TLSv1_server_method,TLSv1_client_method,SSLv3_method,SSLv3_server_method,SSLv3_client_method,SSLv2_method,SSLv2_server_method,SSLv2_client_method,DTLS_method,DTLS_server_method,DTLS_client_method,DTLSv1_2_method,DTLSv1_2_server_method,DTLSv1_2_client_method, DTLSv1_method,DTLSv1_server_method,DTLSv1_client_method - 创建一个新的SSL_CTX对象作为启用TLS/SSL的功能的框架
    
  • 概要

    #include <openssl/ssl.h>
    
    SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);
    const SSL_METHOD *SSLv23_method(void);
    const SSL_METHOD *SSLv23_server_method(void);
    const SSL_METHOD *SSLv23_client_method(void);
    const SSL_METHOD *TLSv1_2_method(void);
    const SSL_METHOD *TLSv1_2_server_method(void);
    const SSL_METHOD *TLSv1_2_client_method(void);
    const SSL_METHOD *TLSv1_1_method(void);
    const SSL_METHOD *TLSv1_1_server_method(void);
    const SSL_METHOD *TLSv1_1_client_method(void);
    const SSL_METHOD *TLSv1_method(void);
    const SSL_METHOD *TLSv1_server_method(void);
    const SSL_METHOD *TLSv1_client_method(void);
    #ifndef OPENSSL_NO_SSL3_METHOD
    const SSL_METHOD *SSLv3_method(void);
    const SSL_METHOD *SSLv3_server_method(void);
    const SSL_METHOD *SSLv3_client_method(void);
    #endif
    #ifndef OPENSSL_NO_SSL2
    const SSL_METHOD *SSLv2_method(void);
    const SSL_METHOD *SSLv2_server_method(void);
    const SSL_METHOD *SSLv2_client_method(void);
    #endif
    
    const SSL_METHOD *DTLS_method(void);
    const SSL_METHOD *DTLS_server_method(void);
    const SSL_METHOD *DTLS_client_method(void);
    const SSL_METHOD *DTLSv1_2_method(void);
    const SSL_METHOD *DTLSv1_2_server_method(void);
    const SSL_METHOD *DTLSv1_2_client_method(void);
    const SSL_METHOD *DTLSv1_method(void);
    const SSL_METHOD *DTLSv1_server_method(void);
    const SSL_METHOD *DTLSv1_client_method(void);
    
  • 描述

    SSL_CTX_new()创建一个新的SSL_CTX对象作为框架,以建立启用TLS/SSL的连接。

  • 备注

    SSL_CTX对象使用method作为连接方法。这些方法以通用类型(供客户端和服务器使用),仅服务器类型和仅客户端类型存在。方法可以是以下类型:

    SSLv23_method(),SSLv23_server_method(),SSLv23_client_method()
    这些是通用的、版本灵活的SSL/TLS方法。实际使用的协议版本将协商到客户端和服务器相互支持的最高版本。支持的协议有SSLv2、SSLv3、TLSv1、TLSv1.1和TLSv1.2。大多数应用程序都应该使用这些方法,并避免使用下面描述的特定于版本的方法。

    可以使用SSL_CTX_set_options (3)或SSL_set_options(3)函数的SSL_OP_NO_SSLv2,SSL_OP_NO_SSLv3,SSL_OP_NO_TLSv1,SSL_OP_NO_TLSv1_1和SSL_OP_NO_TLSv1_2选项进一步限制可用的协议列表。客户端应避免在其支持的协议集中创建“漏洞”,禁用协议时,请确保您还禁用所有先前或所有后续协议版本。在客户端中,当禁用协议版本而不禁用所有先前的协议版本时,其作用是也禁用所有后续的协议版本。

    SSLv2和SSLv3协议已弃用,通常不应使用。应用程序通常应结合使用SSL_CTX_set_options (3)和SSL_OP_NO_SSLv3标志,以通过上述版本灵活的 SSL / TLS方法禁用SSLv3的协商。该SSL_OP_NO_SSLv2选项是默认设置,并且将需要通过清除SSL_CTX_clear_options(3)为了使的SSLv2的谈判。

    TLSv1_2_method(),TLSv1_2_server_method(),TLSv1_2_client_method()
    使用这些方法建立的TLS/SSL连接将仅了解TLSv1.2协议。客户端将发送TLSv1.2客户端问候消息,并且还将指示其仅了解TLSv1.2。服务器将仅了解TLSv1.2客户端问候消息。

    TLSv1_1_method(),TLSv1_1_server_method(),TLSv1_1_client_method()
    使用这些方法建立的TLS/SSL连接将仅了解TLSv1.1协议。客户端将发送TLSv1.1客户端问候消息,并且还将指示其仅了解TLSv1.1。服务器将仅了解TLSv1.1客户端问候消息。

    TLSv1_method(),TLSv1_server_method(),TLSv1_client_method()
    使用这些方法建立的TLS/SSL连接将仅了解TLSv1协议。客户端将发出TLSv1客户端问候消息,并指示其仅理解TLSv1。服务器将仅了解TLSv1客户端问候消息。

    SSLv3_method(),SSLv3_server_method(),SSLv3_client_method()
    使用这些方法建立的TLS/SSL连接将仅了解SSLv3协议。客户端将发出SSLv3客户端问候消息,并指示其仅理解SSLv3。服务器将仅了解SSLv3客户端问候消息。SSLv3协议已被弃用,不应使用。

    SSLv2_method(),SSLv2_server_method(),SSLv2_client_method()
    使用这些方法建立的TLS/SSL连接将仅了解SSLv2协议。客户端将发出SSLv2客户端问候消息,并且还将指示其仅了解SSLv2。服务器将仅了解SSLv2客户端问候消息。SSLv2协议提供的安全性很少甚至没有,因此不应该使用。从OpenSSL 1.0.2g开始,SSLv2不再提供EXPORT密码和56位DES。

    DTLS_method(),DTLS_server_method(),DTLS_client_method()
    这些是版本灵活的DTLS方法。

    DTLSv1_2_method(),DTLSv1_2_server_method(),DTLSv1_2_client_method()
    这些是DTLSv1.2的特定于版本的方法。

    DTLSv1_method(),DTLSv1_server_method(),DTLSv1_client_method()
    这些是DTLSv1的特定于版本的方法。

    SSL_CTX_new()初始化密码列表,会话缓存设置,回调,密钥和证书以及其默认值的选项。

  • 返回值

    可能会出现以下返回值:

    空值
    创建新的SSL_CTX对象失败。检查错误堆栈以找出原因。
    
    指向SSL_CTX对象的指针
    返回值指向已分配的SSL_CTX对象。
    

SSLv23_server_method()

于上 SSL_CTX_new()

const SSL_METHOD *SSLv23_server_method(void);

SSLv23_client_method()

于上 SSL_CTX_new()

const SSL_METHOD *SSLv23_client_method(void);

SSL_CTX_use_certificate()

  • 名称

    SSL_CTX_use_certificate、SSL_CTX_use_certificate_ASN1 SSL_CTX_use_certificate_file、SSL_use_certificate SSL_use_certificate_ASN1, SSL_use_certificate_file, SSL_CTX_use_certificate_chain_file, SSL_CTX_use_PrivateKey, SSL_CTX_use_PrivateKey_ASN1, SSL_CTX_use_PrivateKey_file, SSL_CTX_use_RSAPrivateKey, SSL_CTX_use_RSAPrivateKey_ASN1, SSL_CTX_use_RSAPrivateKey_file, SSL_use_PrivateKey_file, SSL_use_PrivateKey_ASN1, SSL_use_PrivateKey, SSL_use_RSAPrivateKey, SSL_use_RSAPrivateKey_ASN1,SSL_use_RSAPrivateKey_file, SSL_CTX_check_private_key, SSL_check_private_key - 加载证书和密钥数据
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);
    int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, unsigned char *d);
    int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);
    int SSL_use_certificate(SSL *ssl, X509 *x);
    int SSL_use_certificate_ASN1(SSL *ssl, unsigned char *d, int len);
    int SSL_use_certificate_file(SSL *ssl, const char *file, int type);
    
    int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file);
    
    int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey);
    int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, unsigned char *d,
                                    long len);
    int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
    int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa);
    int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, unsigned char *d, long len);
    int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type);
    int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey);
    int SSL_use_PrivateKey_ASN1(int pk,SSL *ssl, unsigned char *d, long len);
    int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type);
    int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa);
    int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, unsigned char *d, long len);
    int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type);
    
    int SSL_CTX_check_private_key(const SSL_CTX *ctx);
    int SSL_check_private_key(const SSL *ssl);
    
  • 描述

    这些函数分别将证书和私钥加载到SSL_CTX或SSL对象中。

    函数的SSL_CTX_*类将证书和密钥加载到SSL_CTX对象ctx中。这些信息通过复制被传递到SSL对象,SSL是通过复制从ctx使用SSL_new(3)创建的,因此应用于ctx的更改不会传播到已经存在的SSL对象。

    函数的SSL_*类只将证书和密钥加载到特定的SSL对象中。当为这个SSL对象调用SSL_clear(3)时,将保留特定的信息。

    SSL_CTX_use_certificate()将证书x加载到ctx中,SSL_use_certificate()将x加载到ssl中。可以使用SSL_CTX_add_extra_chain_cert(3)函数指定形成完整证书链所需的其余证书。

    SSL_CTX_use_certificate_ASN1()将ASN1编码的证书从内存位置d(带长度len)加载到ctx, SSL_use_certificate_ASN1()将ASN1编码的证书加载到ssl。

    SSL_CTX_use_certificate_file()将第一个存储在文件中的证书加载到ctx。证书的格式化类型必须从已知的类型SSL_FILETYPE_PEM、SSL_FILETYPE_ASN1中指定。SSL_use_certificate_file()将证书从文件加载到ssl。请参阅说明部分,了解为什么应该首选SSL_CTX_use_certificate_chain_file()。
    SSL_CTX_use_certificate_chain_file()将证书链从文件加载到ctx。证书必须是PEM格式,并且必须从主题的证书(实际的客户端或服务器证书)开始排序,然后是中间的CA证书(如果适用的话),并在最高级别(根)CA结束。在单个SSL对象上没有相应的函数。

    SSL_CTX_use_PrivateKey()将pkey作为私钥添加到ctx。SSL_CTX_use_RSAPrivateKey()将rsa类型的私钥rsa添加到ctx。SSL_use_PrivateKey()将pkey作为私钥添加到ssl;SSL_use_RSAPrivateKey()将rsa作为rsa类型的私钥添加到ssl。如果已经设置了证书,并且私有证书不属于该证书,则返回一个错误。要更改证书,需要使用SSL_CTX_use_certificate()或SSL_CTX_use_certificate()设置新证书的私钥对,然后再使用SSL_CTX_use_PrivateKey()或SSL_use_PrivateKey()设置私钥。

    SSL_CTX_use_PrivateKey_ASN1()将存储在内存位置d的pk类型私钥(长度len)添加到ctx。SSL_CTX_use_RSAPrivateKey_ASN1()将存储在内存位置d的RSA类型的私钥(长度len)添加到ctx。SSL_use_PrivateKey_ASN1()和SSL_use_RSAPrivateKey_ASN1()将私钥添加到ssl。

    SSL_CTX_use_PrivateKey_file()将文件中找到的第一个私钥添加到ctx。证书的格式化类型必须从已知的类型SSL_FILETYPE_PEM、SSL_FILETYPE_ASN1中指定。

    SSL_CTX_use_RSAPrivateKey_file()将文件中找到的第一个私有RSA密钥添加到ctx。SSL_use_PrivateKey_file()将文件中找到的第一个私钥添加到ssl;SSL_use_RSAPrivateKey_file()将找到的第一个私有RSA密钥添加到ssl。

    SSL_CTX_check_private_key()检查私钥与装载到ctx中的相应证书的一致性。如果安装了多个密钥/证书对(RSA/DSA),将检查最后安装的项。如果最后一项是RSA证书或密钥,将检查RSA密钥/证书对。SSL_check_private_key()对ssl执行相同的检查。如果没有为此ssl显式添加密钥/证书,则将检查添加到ctx中的最后一项。

  • 备注

    OpenSSL的内部证书存储一次可以保存几个私钥/证书对。使用的证书取决于选择的密码,请参见SSL_CTX_set_cipher_list(3)。

    当从文件中读取证书和私钥时,SSL_FILETYPE_ASN1类型的文件(也称为DER,二进制编码)只能包含一个证书或私钥,因此SSL_CTX_use_certificate_chain_file()只适用于PEM格式。SSL_FILETYPE_PEM类型的文件可以包含多个项。

    SSL_CTX_use_certificate_chain_file()将文件中找到的第一个证书添加到证书存储区。其他证书使用SSL_CTX_add1_chain_cert(3)添加到链证书存储中。注意:在1.0.2之前的版本中,所有证书类型只有一个证书连锁店,OpenSSL 1.0.2以及之后的版本中,每个类型都有一个单独的连锁店。

    SSL_CTX_use_certificate_chain_file()应该使用而不是SSL_CTX_use_certificate_file()函数以允许使用完整的证书链,即使在没有受信任的CA时使用或存储CA签发不得添加到受信任的CA证书存储。

    如果在TLS协商期间需要额外的证书来完成链,还会在受信任的CA证书的位置查找CA证书,请参见SSL_CTX_load_verify_locations(3)。

    可以对从文件中加载的私钥进行加密。为了成功加载加密密钥,必须提供一个返回密码的函数,请参见SSL_CTX_set_default_passwd_cb(3)。(从技术角度看,证书文件也可以加密,但是这没有意义,因为证书中的数据无论如何都被认为是公共的。)

    所有的函数来设置一个新的证书将取代任何现有的证书已经设置的相同类型的。同样的所有函数来设置一个新的私钥将取代任何私钥已经设置,应用程序应该叫SSL_CTX_check_private_key(3)或SSL_check_private_key(3)适当的加载一个新的证书和私钥后确认证书和关键比赛。

  • 返回值

    如果成功,函数返回1。否则检查错误堆栈找出原因。
    

SSL_CTX_use_certificate_file()

于上 SSL_CTX_use_certificate()

int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);

SSL_CTX_use_PrivateKey_file()

于上 SSL_CTX_use_certificate()

int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);

SSL_CTX_check_private_key()

于上 SSL_CTX_use_certificate()

int SSL_CTX_check_private_key(const SSL_CTX *ctx);

SSL_new()

  • 名称

    SSL_new - 为连接创建一个新的SSL结构
    
  • 概要

    #include <openssl/ssl.h>
    
    SSL *SSL_new(SSL_CTX *ctx);
    
  • 描述

    SSL_new()创建一个新的SSL结构,该结构用于保存TLS/SSL连接的数据。新结构继承了底层上下文ctx: connection method (SSLv2/v3/TLSv1)、选项、验证设置、超时设置的设置。
    
  • 返回值

    可以出现以下返回值:

    NULL
    新SSL结构的创建失败。检查错误堆栈,找出原因。
    
    指向SSL结构的指针
    返回值指向分配的SSL结构。
    

SSL_set_fd()

  • 名称

    SSL_set_fd - 用文件描述符连接SSL对象
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_set_fd(SSL *ssl, int fd);
    int SSL_set_rfd(SSL *ssl, int fd);
    int SSL_set_wfd(SSL *ssl, int fd);
    
  • 描述

    SSL_set_fd()将文件描述符fd设置为SSL的TLS/SSL(加密)端输入/输出设施。fd通常是网络连接的套接字文件描述符。
    在执行操作时,将自动创建套接字BIO来连接ssl和fd。BIO和SSL引擎继承fd的行为。如果fd是非阻塞的,ssl也将具有非阻塞行为。

    如果已经有一个BIO连接到ssl,那么将调用BIO_free()(对于读写端,如果不同的话)。
    SSL_set_rfd()和SSL_set_wfd()执行各自的操作,但仅针对可独立设置的读通道或写通道。

  • 返回值

    可以出现以下返回值:

    0
    操作失败。检查错误堆栈,找出原因。
    
    1
    操作成功。
    

SSL_accept()

  • 名称

    SSL_accept - 等待TLS/SSL客户机发起TLS/SSL握手
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_accept(SSL *ssl);
    
  • 描述

    SSL_accept()等待TLS/SSL客户机启动TLS/SSL握手。通过设置底层BIO,通信通道必须已经被设置并分配给ssl。

  • 备注

    SSL_accept()的行为取决于底层BIO。
    如果底层BIO被阻塞,SSL_accept()将只在握手完成或发生错误时返回。

    如果底层BIO是非阻塞的,那么当底层BIO不能满足SSL_accept()继续握手的需求时,SSL_accept()也会返回,通过返回值-1指示问题。
    在这种情况下,使用SSL_accept()返回值调用SSL_get_error()将生成SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE。

    然后,调用过程必须在采取适当的操作以满足SSL_accept()的需求之后重复调用。操作取决于底层的BIO。当使用非阻塞套接字时,不需要执行任何操作,但是可以使用select()检查所需的条件。
    在使用缓冲BIO(比如BIO对)时,必须将数据写入BIO或从BIO检索数据,然后才能继续。

  • 返回值

    可以出现以下返回值:

    0
    TLS/SSL握手未成功,但已由TLS/SSL协议的规范控制并关闭。使用返回值ret调用SSL_get_error()来找出原因。
    
    1
    TLS/SSL握手成功完成,已经建立了TLS/SSL连接。
    
    <0
    TLS/SSL握手不成功,因为在协议级别发生了致命错误或连接失败。政府关闭并不干净。它还可以发生动作,是需要继续操作的非阻塞Bios。使用返回值ret调用SSL_get_error()来找出原因。
    

SSL_connect()

  • 名称

    SSL_connect - 使用TLS/SSL服务器启动TLS/SSL握手
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_connect(SSL *ssl);
    
  • 描述

    SSL_connect()启动与服务器的TLS/SSL握手。通过设置底层BIO,通信通道必须已经被设置并分配给ssl。

  • 备注

    SSL_connect()的行为取决于底层BIO。
    如果底层BIO被阻塞,SSL_connect()将只在握手完成或发生错误时返回。

    如果底层BIO是非阻塞的,那么当底层BIO不能满足SSL_connect()继续握手的需求时,SSL_connect()也会返回,通过返回值-1指示问题。在这种情况下,使用SSL_connect()的返回值调用SSL_get_error()将生成SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE。

    然后,调用过程必须在采取适当的操作以满足SSL_connect()的需要之后重复调用。操作取决于底层的BIO。当使用非阻塞套接字时,不需要执行任何操作,但是可以使用select()检查所需的条件。在使用缓冲BIO(比如BIO对)时,必须将数据写入BIO或从BIO检索数据,然后才能继续。

  • 返回值

    可以出现以下返回值:

    0
    TLS/SSL握手未成功,但已由TLS/SSL协议的规范控制并关闭。使用返回值ret调用SSL_get_error()来找出原因。
    
    1
    TLS/SSL握手成功完成,已经建立了TLS/SSL连接。
    
    <0
    TLS/SSL握手不成功,因为在协议级别发生了致命错误,或者发生了连接失败。政府关闭并不干净。它还可以发生动作,是需要继续操作的非阻塞BIOs。使用返回值ret调用SSL_get_error()来找出原因。
    

SSL_read()

  • 名称

    SSL_read - 从TLS/SSL连接读取字节。
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_read(SSL *ssl, void *buf, int num);
    
  • 描述

    SSL_read()尝试从指定的ssl读取num字节到缓冲区buf。

  • 备注

    如果需要,SSL_read()将协商一个TLS/SSL会话,如果SSL_connect(3)或SSL_accept(3)还没有显式执行该会话的话。如果对等方请求重新协商,它将在SSL_read()操作期间透明地执行。SSL_read()的行为取决于底层BIO。

    要使透明协商成功,必须将ssl初始化为客户机或服务器模式。这是通过在第一次调用SSL_read()或SSL_write(3)函数之前调用SSL_set_connect_state(3)或SSL_set_accept_state()来实现的。

    SSL_read()基于SSL/TLS记录工作。数据以记录的形式接收(SSLv3/TLSv1的最大记录大小为16kB)。只有当一条记录被完全接收时,才能对其进行处理(解密和完整性检查)。因此,在最后一次调用SSL_read()时没有检索到的数据仍然可以在SSL层中进行缓冲,并在下一次调用SSL_read()时检索到。如果num大于缓冲的字节数,那么SSL_read()将返回缓冲的字节。

    如果缓冲区中没有更多的字节,那么SSL_read()将触发对下一条记录的处理。只有在接收并完全处理了记录之后,SSL_read()才会返回报告成功。记录的内容最多将被返回。由于SSL/TLS记录的大小可能会超过底层传输(例如TCP)的最大数据包大小,因此在记录完成和SSL_read()可以成功之前,可能需要从传输层读取几个数据包。

    如果底层BIO被阻塞,那么SSL_read()只会在读取操作完成或发生错误时返回,除非发生重新协商,在这种情况下可能会发生SSL_ERROR_WANT_READ。这种行为可以通过SSL_CTX_set_mode(3)调用的SSL_MODE_AUTO_RETRY标志来控制。
    如果底层BIO是非阻塞的,那么当底层BIO不能满足SSL_read()继续操作的需求时,SSL_read()也会返回。在这种情况下,使用SSL_read()的返回值调用SSL_get_error(3)将生成SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE。
    因为任何时候都有可能重新协商,所以调用SSL_read()也会导致写操作!然后,在采取适当的操作以满足SSL_read()的需要之后,调用过程必须重复调用。

    操作取决于底层的BIO。当使用非阻塞套接字时,不需要执行任何操作,但是可以使用select()检查所需的条件。在使用缓冲BIO(比如BIO对)时,必须将数据写入BIO或从BIO检索数据,然后才能继续。
    可以使用SSL_pending(3)来确定是否有可立即检索的缓冲字节。在这种情况下,可以调用SSL_read(),而不需要阻塞底层套接字或从底层套接字接收新数据。

  • 警告

    当由于SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE而必须重复SSL_read()操作时,必须使用相同的参数重复该操作。
    
  • 返回值

    可以出现以下返回值:

    >0
    读取操作成功。返回值是实际从TLS/SSL连接读取的字节数。
    
    <=0
    <0
    读取操作未成功,因为要么连接已关闭,要么发生错误,要么调用进程必须采取操作。使用返回值ret调用SSL_get_error(3)来找出原因。
    SSLv2(不支持)不支持关闭警报协议,因此只能检测底层连接是否关闭。它不能被检查,不管闭包是由对等点发起的还是由其他什么东西发起的。
    旧的文档表明0和-1之间存在差异,并且-1可以重试。您应该调用SSL_get_error()来确定它是否可以重试。
    

SSL_shutdown()

  • 名称

    SSL_shutdown - 关闭TLS/SSL连接
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_shutdown(SSL *ssl);
    
  • 描述

    SSL_shutdown()关闭活动的TLS/SSL连接。它向对等方发送“关闭通知”关闭警报。

  • 备注

    SSL_shutdown()尝试向对等方发送“关闭通知”关闭警报。无论操作成功与否,都会设置SSL_SENT_SHUTDOWN标志,并认为当前打开的会话是关闭的,状态良好,将保存在会话缓存中以供进一步重用。
    注意,如果在连接上发生了以前的致命错误,即如果SSL_get_error()返回了SSL_ERROR_SYSCALL或SSL_ERROR_SSL,则一定不能调用SSL_shutdown()。

    关机过程包括两个步骤:发送“关闭通知”关机警报和接收对方的“关闭通知”关机警报。根据TLS标准,应用程序只发送关闭警报,然后在不等待对等方响应的情况下关闭底层连接,这是可以接受的(这样可以节省资源,因为进程可以终止或服务于另一个连接)。当底层连接应使用用于更多通信时,必须执行完整的关闭过程(双向“关闭通知”警报),以便对等节点保持同步。

    SSL_shutdown()通过其2步行为支持单步关闭和双向关闭。

    当应用程序是第一个发送“关闭通知”警报的一方时,SSL_shutdown()将只发送警报,然后设置SSL_SENT_SHUTDOWN标志(以便认为会话是良好的,并将保存在缓存中)。然后,SSL_shutdown()将返回0。如果单向关闭就足够了(底层连接无论如何都应该关闭),那么第一次调用SSL_shutdown()就足够了。

    为了完成双向关机握手,必须再次调用SSL_shutdown()。第二次调用将使SSL_shutdown()等待同伴的“关闭通知”关闭警报。如果成功,第二个对SSL_shutdown()的调用将返回1。

    如果对方已经发送了“关闭通知”警报,并且已经在另一个函数(SSL_read(3))中隐式地处理了该警报,则设置SSL_RECEIVED_SHUTDOWN标志。SSL_shutdown()将发送“关闭通知”警报,设置SSL_SENT_SHUTDOWN标志,并立即返回1。可以使用SSL_get_shutdown()检查SSL_RECEIVED_SHUTDOWN(3)是否已经设置。
    因此建议,检查SSL_shutdown的返回值()和调用SSL_shutdown(),如果没有完成双向关闭(第一次调用的返回值是0)。关闭不是特别处理SSLv2的站点时的协议,SSL_shutdown()将在第一次调用成功。
    SSL_shutdown()的行为还取决于底层的BIO。
    如果底层BIO被阻塞,SSL_shutdown()只会在握手步骤完成或发生错误时返回。

    如果底层BIO是非阻塞的,那么当底层BIO不能满足SSL_shutdown()继续握手的需要时,SSL_shutdown()也会返回。在这种情况下,使用SSL_shutdown()的返回值调用SSL_get_error()将生成SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE。
    然后,在采取适当的操作以满足SSL_shutdown()的需要之后,调用进程必须重复调用。操作取决于底层的BIO。当使用非阻塞套接字时,不需要执行任何操作,但是可以使用select()检查所需的条件。在使用缓冲BIO(比如BIO对)时,必须将数据写入BIO或从BIO检索数据,然后才能继续。

    可以将SSL_shutdown()修改为仅将连接设置为“shutdown”状态,而不实际发送“close notify”警报消息,请参见SSL_CTX_set_quiet_shutdown(3)。当启用“静默关闭”时,SSL_shutdown()将始终成功并返回1。

  • 返回值

    可以出现以下返回值:

    0
    关闭还没有结束。如果需要执行双向关闭,则再次调用SSL_shutdown()。SSL_get_error(3)的输出可能具有误导性,因为即使没有发生错误,也可能会标记一个错误的SSL_ERROR_SYSCALL。
    
    1
    关闭成功完成。发送了“关闭通知”警报,接收了对等方的“关闭通知”警报。
    
    <0
    关闭不成功,因为在协议级别发生了致命错误,或者发生了连接失败。如果需要继续对非阻塞BIOs执行操作,也会发生这种情况。使用返回值ret调用SSL_get_error(3)来找出原因。
    

SSL_write()

  • 名称

    SSL_write - 将字节写到TLS/SSL连接。
    
  • 概要

    #include <openssl/ssl.h>
    
    int SSL_write(SSL *ssl, const void *buf, int num);
    
  • 描述

    SSL_write()将num字节从缓冲区buf写入指定的ssl连接。

  • 备注

    如果需要,SSL_write()将协商一个TLS/SSL会话,如果SSL_connect(3)或SSL_accept(3)还没有显式执行该会话的话。如果对等方请求重新协商,它将在SSL_write()操作期间透明地执行。SSL_write()的行为取决于底层的BIO。

    要使透明协商成功,必须将ssl初始化为客户机或服务器模式。这是通过在第一次调用SSL_read(3)或SSL_write()函数之前调用SSL_set_connect_state(3)或SSL_set_accept_state()来实现的。
    如果底层BIO被阻塞,那么SSL_write()只会在写操作完成或发生错误时返回,除非发生重新协商,在这种情况下可能会发生SSL_ERROR_WANT_READ。这种行为可以通过SSL_CTX_set_mode(3)调用的SSL_MODE_AUTO_RETRY标志来控制。

    如果底层BIO是非阻塞的,当底层BIO不能满足SSL_write()继续操作的需要时,SSL_write()也会返回。在这种情况下,使用SSL_write()的返回值调用SSL_get_error(3)将生成SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE。
    因为任何时候都有可能重新协商,所以调用SSL_write()也会导致读取操作!然后,调用过程必须在采取适当的操作以满足SSL_write()的需要之后重复调用。操作取决于底层的BIO。当使用非阻塞套接字时,不需要执行任何操作,但是可以使用select()检查所需的条件。在使用缓冲BIO(比如BIO对)时,必须将数据写入BIO或从BIO检索数据,然后才能继续。

    当写入长度为num的buf的完整内容时,SSL_write()才会成功返回。可以使用SSL_CTX_set_mode(3)的SSL_MODE_ENABLE_PARTIAL_WRITE选项更改此默认行为。当设置此标志时,当部分写操作成功完成时,SSL_write()也将成功返回。在本例中,SSL_write()操作被认为已经完成。
    这些字节被发送,必须启动一个新的带有新缓冲区的SSL_write()操作(已发送的字节已被删除)。执行消息块大小的部分写操作,SSLv3/TLSv1的消息块大小为16kB。

  • 警告

    当由于SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE而必须重复SSL_write()操作时,必须使用相同的参数重复该操作。
    当调用发送num=0字节的SSL_write()时,行为是未定义的。
    
  • 返回值

    可以出现以下返回值:

    >0
    写入操作成功,返回值是实际写入到TLS/SSL连接的字节数。
    
    <= 0
    写操作未成功,因为要么连接已关闭,要么发生错误,要么调用进程必须采取操作。使用返回值ret调用SSL_get_error()来找出原因。
    SSLv2(不支持)不支持关闭警报协议,因此只能检测底层连接是否关闭。它不能被检查,为什么关闭发生。
    旧的文档表明0和-1之间存在差异,并且-1可以重试。您应该调用SSL_get_error()来确定它是否可以重试。
    

SSL_free()

  • 名称

    SSL_free - 释放一个已分配的SSL结构
    
  • 概要

    #include <openssl/ssl.h>
    
    void SSL_free(SSL *ssl);
    
  • 描述

    SSL_free()减少ssl的引用计数,并删除ssl指向的ssl结构,如果引用计数达到0,则释放分配的内存。

  • 备注

    SSL_free()还为间接受影响的项调用free()过程(如果适用的话):缓冲BIO、读写BIOs、为这个ssl专门创建的密码列表、SSL_SESSION。不要在调用SSL_free()之前或之后显式释放这些间接释放的项,因为尝试释放两次可能导致程序失败。

    ssl会话具有来自两个用户的引用计数:ssl对象,其中引用计数由SSL_free()和内部会话缓存删除。如果会话被认为是坏的,因为连接没有调用SSL_shutdown(3),也没有使用SSL_set_shutdown(3)来设置SSL_SENT_SHUTDOWN状态,那么会话也将按照RFC2246的要求从会话缓存中删除。

  • 返回值

    SSL_free()不提供诊断信息。
    

SSL_CTX_free()

  • 名称

    SSL_CTX_free - 释放一个已分配的SSL_CTX对象
    
  • 概要

    #include <openssl/ssl.h>
    
    void SSL_CTX_free(SSL_CTX *ctx);
    
  • 描述

    SSL_CTX_free()减少ctx的引用计数,并删除ctx指向的SSL_CTX对象,如果引用计数达到0,则释放分配的内存。
    它还调用间接受影响项的free()过程(如果适用的话):会话缓存、密码列表、客户端ca列表、证书和密钥。

  • 警告

    如果设置了一个session-remove回调(SSL_CTX_sess_set_remove_cb()),这个回调将为从ctx的会话缓存中释放的每个会话调用。这意味着,来自外部会话缓存的所有相应会话也将被删除。

    如果不希望这样,用户应该在调用SSL_CTX_sess_set_remove_cb(ctx, NULL)之前显式地取消对回调的设置。

  • 返回值

    可以出现以下返回值:

    SSL_CTX_free()不提供诊断信息。
    

以上是关于基于 OpenSSL 的简单 C/S 通信 C 程序设计使用函数文档的主要内容,如果未能解决你的问题,请参考以下文章

加密和解密技术基础与OpenSSL

第三模块:网络编程

轻量级通信引擎StriveEngine —— C/S通信demo —— 使用二进制协议 (附源码)

c/s架构搭建

基于socket构造c/s 架构软件

socket 简单c/s通信