如何在没有套接字管理的情况下使用 OpenSSL 库?

Syl*_*ter 2 c c++ sockets openssl

我正在通过向其添加完整的加密功能来改进现有的 C++ 通信模块。我想使用 OpenSSL 的全部功能,如握手、密钥协商、密钥生成和加密,以确保服务器和客户端之间创建的通道安全。

我的问题是,我不想放弃现有的套接字管理方法,而只是使用 OpenSSL 来完成这项任务。我的模块必须仍然控制文件描述符、套接字构造/销毁和消息发送/接收。

我最近阅读了很多 OpenSSL 的源代码,我注意到它使用了一个状态机。状态机启动消息的构建和发送,如用于握手的客户端 hello。我试图弄清楚如何防止状态机发送/接收这些消息,同时仍然保持其功能。

我注意到 OpenSSL 使用了许多在 ssl_method_st 中找到的函数指针,这些函数指针存储在 ssl_st 结构中。


    /* Used to hold SSL/TLS functions */
    struct ssl_method_st {
        int version;
        unsigned flags;
        unsigned long mask;
        int (*ssl_new) (SSL *s);
        int (*ssl_clear) (SSL *s);
        void (*ssl_free) (SSL *s);
        int (*ssl_accept) (SSL *s);
        int (*ssl_connect) (SSL *s);
        int (*ssl_read) (SSL *s, void *buf, size_t len, size_t *readbytes);
        int (*ssl_peek) (SSL *s, void *buf, size_t len, size_t *readbytes);
        int (*ssl_write) (SSL *s, const void *buf, size_t len, size_t *written);
        int (*ssl_shutdown) (SSL *s);
        int (*ssl_renegotiate) (SSL *s);
        int (*ssl_renegotiate_check) (SSL *s, int);
        int (*ssl_read_bytes) (SSL *s, int type, int *recvd_type,
                               unsigned char *buf, size_t len, int peek,
                               size_t *readbytes);
        int (*ssl_write_bytes) (SSL *s, int type, const void *buf_, size_t len,
                                size_t *written);
        int (*ssl_dispatch_alert) (SSL *s);
        long (*ssl_ctrl) (SSL *s, int cmd, long larg, void *parg);
        long (*ssl_ctx_ctrl) (SSL_CTX *ctx, int cmd, long larg, void *parg);
        const SSL_CIPHER *(*get_cipher_by_char) (const unsigned char *ptr);
        int (*put_cipher_by_char) (const SSL_CIPHER *cipher, WPACKET *pkt,
                                   size_t *len);
        size_t (*ssl_pending) (const SSL *s);
        int (*num_ciphers) (void);
        const SSL_CIPHER *(*get_cipher) (unsigned ncipher);
        long (*get_timeout) (void);
        const struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */
        int (*ssl_version) (void);
        long (*ssl_callback_ctrl) (SSL *s, int cb_id, void (*fp) (void));
        long (*ssl_ctx_callback_ctrl) (SSL_CTX *s, int cb_id, void (*fp) (void));
    };

Run Code Online (Sandbox Code Playgroud)

也许我可以在不调用 SSL_set_fd 的情况下将其中一些指针设置为我自己的函数?我希望 OpenSSL 在需要时构建和加密所需的消息,例如 clientHello、serverHello,但不是发送它,而是将构建的消息提供给我的模块。

我怎样才能做到这一点?

Sam*_*hik 5

我请你注意BIO_new_bio_pair(3)手册页

BIO 对的一种典型用途是将 TLS/SSL I/O 置于应用程序控制之下,这可以在应用程序希望使用 TLS/SSL 的非标准传输或常规套接字例程不合适时使用。

BIO 是 OpenSSL 的输入/输出源/接收器抽象层。

手册页末尾的简短示例似乎完美地描述了您想要做的事情。总结一下:

  • 创建链接的 BIO 对

  • 使用SSL_set_BIO()一个新的SSL会话连接到BIO对的一半(而不是一个套接字)

  • 正常使用 SSL 会话,并从另一个 BIO 对读取/写入 TLS 加密数据。从这一点开始,您可以获取数据并自己处理对套接字的读/写,或者对它做任何您想做的事情。

BIO 对实际上形成了与双向管道文件描述符非常相似的东西。BIO_new_bio_pair为您提供与您从 中获得的内容pipe(2)相同的东西,只是管道的两端都是双向的。

您必须非常小心并理解的一件事是在内部“BIO 管道”中缓冲的数据的语义,以及如何正确处理它。例如,SSL 级别的例程现在大部分行为就像它们连接到非阻塞文件描述符一样,并在底层“BIO 管道”中没有数据或其内部缓冲区已满时执行相应的行为。