ssl
OpenSSL的SSL/TLS库,实现了SSL(Secur)/TLS(Transport Layer Security)/DTLS(Datagram Transport Layer Security)协议的多个版本。
SSL_CTX对象包含证书、算法等信息,用于建立TLS/SSL连接。
网络连接建立后可以赋值给SSL对象,然后可以使用SSL对象完成握手操作(SSL_accept或SSL_connect或SSL_do_handshake),握手完成后就可以读写了。关闭网络连接前先调用SSL_shutdown关闭TLS/SSL连接。
- SSL_METHOD 特定协议的操作函数,创建SSL_CTX时需要
- SSL_CIPHER 加密算法信息,可以在SSL_CTX中指定可用的算法集合,SSL_SESSION中会记录实际使用的算法。
- SSL_CTX 全局配置,包含SSL配置的默认值。
- SSL_SESSION 包含当前session信息的结构,包括:加密算法、证书、密钥等
- SSL SSL连接,
使用证书和密钥
#include <openssl/ssl.h>
/* CA 设置 */
int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath);
int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile);
int SSL_CTX_load_verify_store(SSL_CTX *ctx, const char *CAstore);
int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx);
int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx);
int SSL_CTX_set_default_verify_file(SSL_CTX *ctx);
int SSL_CTX_set_default_verify_store(SSL_CTX *ctx);
int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
const char *CApath);
/* CA证书相关操作 */
void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *list);
void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) *list);
STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *s);
STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx);
int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *cacert);
int SSL_add_client_CA(SSL *ssl, X509 *cacert);
void SSL_CTX_set0_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list);
void SSL_set0_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list);
const STACK_OF(X509_NAME) *SSL_CTX_get0_CA_list(const SSL_CTX *ctx);
const STACK_OF(X509_NAME) *SSL_get0_CA_list(const SSL *s);
int SSL_CTX_add1_to_CA_list(SSL_CTX *ctx, const X509 *x);
int SSL_add1_to_CA_list(SSL *ssl, const X509 *x);
const STACK_OF(X509_NAME) *SSL_get0_peer_CA_list(const SSL *s);
/* 证书链设置 */
int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509);
int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509);
int SSL_CTX_get0_chain_certs(SSL_CTX *ctx, STACK_OF(X509) **sk);
int SSL_CTX_clear_chain_certs(SSL_CTX *ctx);
int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *sk);
int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *sk);
int SSL_add0_chain_cert(SSL *ssl, X509 *x509);
int SSL_add1_chain_cert(SSL *ssl, X509 *x509);
int SSL_get0_chain_certs(SSL *ssl, STACK_OF(X509) **sk);
int SSL_clear_chain_certs(SSL *ssl);
int SSL_CTX_build_cert_chain(SSL_CTX *ctx, flags);
int SSL_build_cert_chain(SSL *ssl, flags);
/* 证书和密钥设置 */
int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);
int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const 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, const 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_use_certificate_chain_file(SSL *ssl, 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, const 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, const 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, const 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, const 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);
int SSL_CTX_use_cert_and_key(SSL_CTX *ctx, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override);
int SSL_use_cert_and_key(SSL *ssl, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override);
/* 证书选择 */
int SSL_CTX_select_current_cert(SSL_CTX *ctx, X509 *x509);
int SSL_select_current_cert(SSL *ssl, X509 *x509);
int SSL_CTX_set_current_cert(SSL_CTX *ctx, long op);
int SSL_set_current_cert(SSL *ssl, long op);
SSL_CTX操作
#include <openssl/ssl.h>
SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq,
const SSL_METHOD *method);
SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);
int SSL_CTX_up_ref(SSL_CTX *ctx);
const SSL_METHOD *TLS_method(void);
const SSL_METHOD *TLS_server_method(void);
const SSL_METHOD *TLS_client_method(void);
const SSL_METHOD *SSLv23_method(void);
const SSL_METHOD *SSLv23_server_method(void);
const SSL_METHOD *SSLv23_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_TLS1_METHOD
const SSL_METHOD *TLSv1_method(void);
const SSL_METHOD *TLSv1_server_method(void);
const SSL_METHOD *TLSv1_client_method(void);
#endif
#ifndef OPENSSL_NO_TLS1_1_METHOD
const SSL_METHOD *TLSv1_1_method(void);
const SSL_METHOD *TLSv1_1_server_method(void);
const SSL_METHOD *TLSv1_1_client_method(void);
#endif
#ifndef OPENSSL_NO_TLS1_2_METHOD
const SSL_METHOD *TLSv1_2_method(void);
const SSL_METHOD *TLSv1_2_server_method(void);
const SSL_METHOD *TLSv1_2_client_method(void);
#endif
const SSL_METHOD *DTLS_method(void);
const SSL_METHOD *DTLS_server_method(void);
const SSL_METHOD *DTLS_client_method(void);
#ifndef OPENSSL_NO_DTLS1_METHOD
const SSL_METHOD *DTLSv1_method(void);
const SSL_METHOD *DTLSv1_server_method(void);
const SSL_METHOD *DTLSv1_client_method(void);
#endif
#ifndef OPENSSL_NO_DTLS1_2_METHOD
const SSL_METHOD *DTLSv1_2_method(void);
const SSL_METHOD *DTLSv1_2_server_method(void);
const SSL_METHOD *DTLSv1_2_client_method(void);
#endif
void SSL_CTX_free(SSL_CTX *ctx);
uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t options);
uint64_t SSL_set_options(SSL *ssl, uint64_t options);
uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t options);
uint64_t SSL_clear_options(SSL *ssl, uint64_t options);
uint64_t SSL_CTX_get_options(const SSL_CTX *ctx);
uint64_t SSL_get_options(const SSL *ssl);
long SSL_get_secure_renegotiation_support(SSL *ssl);
typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);
void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb verify_callback);
void SSL_set_verify(SSL *ssl, int mode, SSL_verify_cb verify_callback);
SSL_get_ex_data_X509_STORE_CTX_idx(void);
void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth);
void SSL_set_verify_depth(SSL *ssl, int depth);
int SSL_verify_client_post_handshake(SSL *ssl);
void SSL_CTX_set_post_handshake_auth(SSL_CTX *ctx, int val);
void SSL_set_post_handshake_auth(SSL *ssl, int val);
int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version);
int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version);
int SSL_CTX_get_min_proto_version(SSL_CTX *ctx);
int SSL_CTX_get_max_proto_version(SSL_CTX *ctx);
int SSL_set_min_proto_version(SSL *ssl, int version);
int SSL_set_max_proto_version(SSL *ssl, int version);
int SSL_get_min_proto_version(SSL *ssl);
int SSL_get_max_proto_version(SSL *ssl);
X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx);
X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl);
int SSL_CTX_set1_param(SSL_CTX *ctx, X509_VERIFY_PARAM *vpm);
int SSL_set1_param(SSL *ssl, X509_VERIFY_PARAM *vpm);
一些SSL_CTX中的设置也可以在SSL中单独设置或修改,接口也一并在此列出。
SSL_CONF_CTX操作
#include <openssl/ssl.h>
SSL_CONF_CTX *SSL_CONF_CTX_new(void);
void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx);
unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags);
unsigned int SSL_CONF_CTX_clear_flags(SSL_CONF_CTX *cctx, unsigned int flags);
unsigned int SSL_CONF_CTX_set1_prefix(SSL_CONF_CTX *cctx, const char *prefix);
int SSL_CONF_cmd(SSL_CONF_CTX *ctx, const char *option, const char *value);
int SSL_CONF_cmd_value_type(SSL_CONF_CTX *ctx, const char *option);
int SSL_CONF_cmd_argv(SSL_CONF_CTX *cctx, int *pargc, char ***pargv);
void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx);
void SSL_CONF_CTX_set_ssl(SSL_CONF_CTX *cctx, SSL *ssl);
SSL基本操作
#include <openssl/ssl.h>
SSL *SSL_new(SSL_CTX *ctx);
SSL *SSL_dup(SSL *s);
int SSL_up_ref(SSL *s);
void SSL_free(SSL *ssl);
int SSL_set_fd(SSL *ssl, int fd);
int SSL_set_rfd(SSL *ssl, int fd);
int SSL_set_wfd(SSL *ssl, int fd);
int SSL_get_fd(const SSL *ssl);
int SSL_get_rfd(const SSL *ssl);
int SSL_get_wfd(const SSL *ssl);
void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio);
void SSL_set0_rbio(SSL *s, BIO *rbio);
void SSL_set0_wbio(SSL *s, BIO *wbio);
BIO *SSL_get_rbio(SSL *ssl);
BIO *SSL_get_wbio(SSL *ssl);
int SSL_accept(SSL *ssl);
int SSL_connect(SSL *ssl);
void SSL_set_connect_state(SSL *ssl);
void SSL_set_accept_state(SSL *ssl);
int SSL_is_server(const SSL *ssl);
int SSL_do_handshake(SSL *ssl);
int SSL_shutdown(SSL *ssl);
int SSL_get_shutdown(const SSL *ssl);
int SSL_clear(SSL *ssl);
ossl_ssize_t SSL_sendfile(SSL *s, int fd, off_t offset, size_t size, int flags);
int SSL_write_ex(SSL *s, const void *buf, size_t num, size_t *written);
int SSL_write(SSL *ssl, const void *buf, int num);
int SSL_pending(const SSL *ssl);
int SSL_has_pending(const SSL *s);
int SSL_read_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
int SSL_read(SSL *ssl, void *buf, int num);
int SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
int SSL_peek(SSL *ssl, void *buf, int num);
int SSL_get_error(const SSL *ssl, int ret);
这些SSL函数不包含socket的基础操作,必须跟系统的socket或BIO结合使用。
The behaviour of SSL_accept() depends on the underlying BIO.
If the underlying BIO is blocking, SSL_accept() will only return once the handshake has been finished or an error occurred.
If the underlying BIO is nonblocking, SSL_accept() will also return when the underlying BIO could not satisfy the needs of
SSL_accept() to continue the handshake, indicating the problem by the return value -1. In this case a call to SSL_get_error() with
the return value of SSL_accept() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. The calling process then must repeat the
call after taking appropriate action to satisfy the needs of SSL_accept(). The action depends on the underlying BIO. When using a
nonblocking socket, nothing is to be done, but select() can be used to check for the required condition. When using a buffering BIO,
like a BIO pair, data must be written into or retrieved out of the BIO before being able to continue.
基于BIO的SSL操作
BIO socket 操作
#include <openssl/bio.h>
const BIO_METHOD *BIO_s_accept(void);
long BIO_set_accept_name(BIO *b, char *name);
char *BIO_get_accept_name(BIO *b);
long BIO_set_accept_port(BIO *b, char *port);
char *BIO_get_accept_port(BIO *b);
BIO *BIO_new_accept(char *host_port);
long BIO_set_nbio_accept(BIO *b, int n);
long BIO_set_accept_bios(BIO *b, char *bio);
char *BIO_get_peer_name(BIO *b);
char *BIO_get_peer_port(BIO *b);
long BIO_get_accept_ip_family(BIO *b);
long BIO_set_accept_ip_family(BIO *b, long family);
long BIO_set_bind_mode(BIO *b, long mode);
long BIO_get_bind_mode(BIO *b);
int BIO_do_accept(BIO *b);
注意:上述操作仅是socket操作,没有涉及SSL相关内容,要实现SSL通信,还需要结合以下SSL操作
SSL BIO操作
#include <openssl/bio.h>
#include <openssl/ssl.h>
const BIO_METHOD *BIO_f_ssl(void);
long BIO_set_ssl(BIO *b, SSL *ssl, long c);
long BIO_get_ssl(BIO *b, SSL **sslp);
long BIO_set_ssl_mode(BIO *b, long client);
long BIO_set_ssl_renegotiate_bytes(BIO *b, long num);
long BIO_set_ssl_renegotiate_timeout(BIO *b, long seconds);
long BIO_get_num_renegotiates(BIO *b);
BIO *BIO_new_ssl(SSL_CTX *ctx, int client);
BIO *BIO_new_ssl_connect(SSL_CTX *ctx);
BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx);
int BIO_ssl_copy_session_id(BIO *to, BIO *from);
void BIO_ssl_shutdown(BIO *bio);
long BIO_do_handshake(BIO *b);
操作是否需要重试
在非阻塞模式下,操作无法立即完成时会返回错误,应用需要判断是需要重试还是真的有错误,判断是否需要重试、重试的原因和重试的操作:
int BIO_should_read(BIO *b);
int BIO_should_write(BIO *b);
int BIO_should_io_special(iBIO *b);
int BIO_retry_type(BIO *b);
int BIO_should_retry(BIO *b);
BIO *BIO_get_retry_BIO(BIO *bio, int *reason);
int BIO_get_retry_reason(BIO *bio);
void BIO_set_retry_reason(BIO *bio, int reason);
关于accept socket的非阻塞模块
设置非阻塞模式:long BIO_set_nbio_accept(BIO *b, int n);
If the underlying accept socket is nonblocking and BIO_do_accept() is called to await an incoming connection it is possible for
BIO_should_io_special() with the reason BIO_RR_ACCEPT. If this happens then it is an indication that an accept attempt would block:
the application should take appropriate action to wait until the underlying socket has accepted a connection and retry the call.
如果accept socket是非阻塞模式,调用BIO_do_accept来等待连接,当前没有连接时会返回错误,BIO_should_retry会返回true,说明操作需要重试,BIO_should_io_special会返回true并且BIO_get_retry_reason会返回 BIO_RR_ACCEPT,这种情况说明accept操作需要阻塞等待,应用程序应该等待直到底层socket接受一个连接后再重试BIO_do_accept。等待可用使用select/poll/epoll来实现。
demo解读
server-arg
- 首先需要构建一个用于server端的SSL_CTX对象ctx:ctx = SSL_CTX_new(TLS_server_method());
- 再构建一个SSL_CONF_CTX对象cctx对ctx进行配置:cctx = SSL_CONF_CTX_new(); SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
- 构建server socket BIO: in = BIO_new_accept(port) 等价于:
BIO * in = BIO_new(BIO_s_accept());
BIO_set_accept_name(in, port)
- 构建一个用于server端连接的BIO:ssl_bio = BIO_new_ssl(ctx, 0);
将ssl_bio指定用于server socket传入连接BIO的处理,每个传入连接会复制ssl_bio对象(链),使用ssl_bio一致的方式处理传入连接,即相当于:int fd = accept();
BIO* conn_io = BIO_new_fd(fd, 1);
ssl_io = BIO_dup_chain(ssl_bio);
BIO_push(ssl_io, conn_io);
- 接受连接:BIO_do_accept(in)
- 接受连接后,进行读写操作BIO_read(in, buf, 512);
新建连接对应的BIO对象被串到accept BIO之后,即accept->socket,此时读写accept BIO对象,相当于读写socket BIO,而没有接受连接之前读写accept BIO,accept BIO在BIO链的末端,会先等待连接,再在socket BIO上执行读写操作。而之前还调用了BIO_set_accept_bios(),BIO链应该是accept->otherbios->socket。 - 连接用完之后要关闭它,先将它从BIO链中移除:tmp = BIO_pop(in);
这里返回的tmp实际是BIO链otherbios->socket,可以关闭它,也可以做其他IO操作,需要并发处理多个连接时,通常在BIO_do_accept之后,执行BIO_pop得到对应连接的BIO,进行后续处理,而accept BIO就可以再次执行BIO_do_accept - 关闭socket BIO:BIO_free_all(tmp);
注意这里是一个BIO链,所以用BIO_free_all全部关闭。 - 最后关闭accept BIO:IO_free(in);
关闭accept BIO的同时会自动关闭之前设置的ssl_bio。
client-arg
- 构建一个用于client端的SSL_CTX对象ctx:ctx = SSL_CTX_new(TLS_client_method());
- 再构建一个SSL_CONF_CTX对象cctx对ctx进行配置:cctx = SSL_CONF_CTX_new(); SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
- 创建用于客户端连接的BIO对象:sbio = BIO_new_ssl_connect(ctx);
- 指定服务端地址端口:BIO_set_conn_hostname(sbio, connect_str);
- 连接到服务器:BIO_do_connect(sbio)
- 发送请求:BIO_puts(sbio, “GET / HTTP/1.0\n\n”);
- 读应答:BIO_read(sbio, tmpbuf, 1024);
- 关闭连接:BIO_free_all(sbio);
|