OpenSSL的源代码包括三部分:加密算法库、SSL库和应用程序。
加密算法库的源代码主要在crypto文件夹里,包括ASN.1编码与解码接口(crypto/asn1/asn1.h),伪随机数产生器(crypto/rand/rand.h),ENGINE机制(crypto/engine),统一密码算法的EVP密码算法接口(crypto/evp/evp.h),大数运算接口(crypto/bn/bn.h),私钥信息语法(crypto/x509/x509.h),非对称密码算法(crypto/rsa/ras.h)等。
SSL库的源代码主要在ssl文件夹里,我们重点分析。
SSL相关的几个重要的数据结构包括:
1、SSL连接结构:核心结构,应用程序通过该结构获取所有其他结构。
2、上下文结构SSL_CTX:是全局的上下文结构,含有结构SSL的主要默认值,包含SSL会话结构。
3、会话结构SSL_SESSION:含有链接的当前TLS/SSL会话细节。
4、密码结构SSL_CIPHER:含有制定的加密算法信息。
5、方法结构SSL_METHOD:功能函数接口,统一了各种SSL协议版本(SSLv1, SSLv2, SSLv3, TLSv1)。
SSL连接结构列出如下:
- struct ssl_st
- {
- /* protocol version
- * (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION, DTLS1_VERSION)
- */
- int version;
- int type; /* SSL_ST_CONNECT or SSL_ST_ACCEPT */
- const SSL_METHOD *method; /* SSLv3 */
- /* There are 2 BIO's even though they are normally both the
- * same. This is so data can be read and written to different
- * handlers */
- #ifndef OPENSSL_NO_BIO
- BIO *rbio; /* used by SSL_read */
- BIO *wbio; /* used by SSL_write */
- BIO *bbio; /* used during session-id reuse to concatenate
- * messages */
- #else
- char *rbio; /* used by SSL_read */
- char *wbio; /* used by SSL_write */
- char *bbio;
- #endif
- /* This holds a variable that indicates what we were doing
- * when a 0 or -1 is returned. This is needed for
- * non-blocking IO so we know what request needs re-doing when
- * in SSL_accept or SSL_connect */
- int rwstate;
- /* true when we are actually in SSL_accept() or SSL_connect() */
- int in_handshake;
- int (*handshake_func)(SSL *);
- /* Imagine that here's a boolean member "init" that is
- * switched as soon as SSL_set_{accept/connect}_state
- * is called for the first time, so that "state" and
- * "handshake_func" are properly initialized. But as
- * handshake_func is == 0 until then, we use this
- * test instead of an "init" member.
- */
- int server; /* are we the server side? - mostly used by SSL_clear*/
- int new_session;/* 1 if we are to use a new session.
- * 2 if we are a server and are inside a handshake
- * (i.e. not just sending a HelloRequest)
- * NB: For servers, the 'new' session may actually be a previously
- * cached session or even the previous session unless
- * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */
- int quiet_shutdown;/* don't send shutdown packets */
- int shutdown; /* we have shut things down, 0x01 sent, 0x02
- * for received */
- int state; /* where we are */
- int rstate; /* where we are when reading */
- BUF_MEM *init_buf; /* buffer used during init */
- void *init_msg; /* pointer to handshake message body, set by ssl3_get_message() */
- int init_num; /* amount read/written */
- int init_off; /* amount read/written */
- /* used internally to point at a raw packet */
- unsigned char *packet;
- unsigned int packet_length;
- struct ssl2_state_st *s2; /* SSLv2 variables */
- struct ssl3_state_st *s3; /* SSLv3 variables */
- struct dtls1_state_st *d1; /* DTLSv1 variables */
- int read_ahead; /* Read as many input bytes as possible
- * (for non-blocking reads) */
- /* callback that allows applications to peek at protocol messages */
- void (*msg_callback)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
- void *msg_callback_arg;
- int hit; /* reusing a previous session */
- X509_VERIFY_PARAM *param;
- #if 0
- int purpose; /* Purpose setting */
- int trust; /* Trust setting */
- #endif
- /* crypto */
- STACK_OF(SSL_CIPHER) *cipher_list;
- STACK_OF(SSL_CIPHER) *cipher_list_by_id;
- /* These are the ones being used, the ones in SSL_SESSION are
- * the ones to be 'copied' into these ones */
- int mac_flags;
- EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
- EVP_MD_CTX *read_hash; /* used for mac generation */
- #ifndef OPENSSL_NO_COMP
- COMP_CTX *expand; /* uncompress */
- #else
- char *expand;
- #endif
- EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */
- EVP_MD_CTX *write_hash; /* used for mac generation */
- #ifndef OPENSSL_NO_COMP
- COMP_CTX *compress; /* compression */
- #else
- char *compress;
- #endif
- /* session info */
- /* client cert? */
- /* This is used to hold the server certificate used */
- struct cert_st /* CERT */ *cert;
- /* the session_id_context is used to ensure sessions are only reused
- * in the appropriate context */
- unsigned int sid_ctx_length;
- unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];
- /* This can also be in the session once a session is established */
- SSL_SESSION *session;
- /* Default generate session ID callback. */
- GEN_SESSION_CB generate_session_id;
- /* Used in SSL2 and SSL3 */
- int verify_mode; /* 0 don't care about verify failure.
- * 1 fail if verify fails */
- int (*verify_callback)(int ok,X509_STORE_CTX *ctx); /* fail if callback returns 0 */
- void (*info_callback)(const SSL *ssl,int type,int val); /* optional informational callback */
- int error; /* error bytes to be written */
- int error_code; /* actual code */
- #ifndef OPENSSL_NO_KRB5
- KSSL_CTX *kssl_ctx; /* Kerberos 5 context */
- #endif /* OPENSSL_NO_KRB5 */
- #ifndef OPENSSL_NO_PSK
- unsigned int (*psk_client_callback)(SSL *ssl, const char *hint, char *identity,
- unsigned int max_identity_len, unsigned char *psk,
- unsigned int max_psk_len);
- unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
- unsigned char *psk, unsigned int max_psk_len);
- #endif
- SSL_CTX *ctx;
- /* set this flag to 1 and a sleep(1) is put into all SSL_read()
- * and SSL_write() calls, good for nbio debuging :-) */
- int debug;
- /* extra application data */
- long verify_result;
- CRYPTO_EX_DATA ex_data;
- /* for server side, keep the list of CA_dn we can use */
- STACK_OF(X509_NAME) *client_CA;
- int references;
- unsigned long options; /* protocol behaviour */
- unsigned long mode; /* API behaviour */
- long max_cert_list;
- int first_packet;
- int client_version; /* what was passed, used for
- * SSLv3/TLS rollback check */
- unsigned int max_send_fragment;
- #ifndef OPENSSL_NO_TLSEXT
- /* TLS extension debug callback */
- void (*tlsext_debug_cb)(SSL *s, int client_server, int type,
- unsigned char *data, int len,
- void *arg);
- void *tlsext_debug_arg;
- char *tlsext_hostname;
- int servername_done; /* no further mod of servername
- 0 : call the servername extension callback.
- 1 : prepare 2, allow last ack just after in server callback.
- 2 : don't call servername callback, no ack in server hello
- */
- /* certificate status request info */
- /* Status type or -1 if no status type */
- int tlsext_status_type;
- /* Expect OCSP CertificateStatus message */
- int tlsext_status_expected;
- /* OCSP status request only */
- STACK_OF(OCSP_RESPID) *tlsext_ocsp_ids;
- X509_EXTENSIONS *tlsext_ocsp_exts;
- /* OCSP response received or to be sent */
- unsigned char *tlsext_ocsp_resp;
- int tlsext_ocsp_resplen;
- /* RFC4507 session ticket expected to be received or sent */
- int tlsext_ticket_expected;
- #ifndef OPENSSL_NO_EC
- size_t tlsext_ecpointformatlist_length;
- unsigned char *tlsext_ecpointformatlist; /* our list */
- size_t tlsext_ellipticcurvelist_length;
- unsigned char *tlsext_ellipticcurvelist; /* our list */
- #endif /* OPENSSL_NO_EC */
- /* draft-rescorla-tls-opaque-prf-input-00.txt information to be used for handshakes */
- void *tlsext_opaque_prf_input;
- size_t tlsext_opaque_prf_input_len;
- /* TLS Session Ticket extension override */
- TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
- /* TLS Session Ticket extension callback */
- tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
- void *tls_session_ticket_ext_cb_arg;
- /* TLS pre-shared secret session resumption */
- tls_session_secret_cb_fn tls_session_secret_cb;
- void *tls_session_secret_cb_arg;
- SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
- #define session_ctx initial_ctx
- #else
- #define session_ctx ctx
- #endif /* OPENSSL_NO_TLSEXT */
- };
列出SSL方法结构SSL_METHOD如下:
- /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
- typedef struct ssl_method_st
- {
- int version;
- int (*ssl_new)(SSL *s);
- void (*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,int len);
- int (*ssl_peek)(SSL *s,void *buf,int len);
- int (*ssl_write)(SSL *s,const void *buf,int len);
- int (*ssl_shutdown)(SSL *s);
- int (*ssl_renegotiate)(SSL *s);
- int (*ssl_renegotiate_check)(SSL *s);
- long (*ssl_get_message)(SSL *s, int st1, int stn, int mt, long
- max, int *ok);
- int (*ssl_read_bytes)(SSL *s, int type, unsigned char *buf, int len,
- int peek);
- int (*ssl_write_bytes)(SSL *s, int type, const void *buf_, int len);
- 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,unsigned char *ptr);
- int (*ssl_pending)(const SSL *s);
- int (*num_ciphers)(void);
- const SSL_CIPHER *(*get_cipher)(unsigned ncipher);
- const struct ssl_method_st *(*get_ssl_method)(int version);
- long (*get_timeout)(void);
- 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));
- } SSL_METHOD;
SSL工作方式:双向证书认证的SSL握手过程。
以下简要介绍SSL协议的工作方式。客户端要收发几个握手信号:
1、发送一个“ClientHello”消息,说明它支持的密码算法列表、压缩方法及最高协议版本,也发送稍后将被使用的随机数。
2、然后收到一个“ServerHello”消息,包含服务器选择的连接参数,源自客户端初期所提供的“ClientHello”。
3、当双方知道了连接参数,客户端与服务器交换证书(依靠被选择的公钥系统)。这些证书通常基于X.509,不过已有草案支持以OpenPGP为基础的证书。
4、服务器请求客户端公钥。客户端有证书即双向身份认证,没证书时随机生成公钥。
5、客户端与服务器通过公钥保密协商共同的主私钥(双方随机协商),这通过精心谨慎设计的伪随机数功能实现。结果可能使用Diffie-Hellman交换,或简化的公钥加密,双方各自用私钥解密。所有其他关键数据的加密均使用这个“主密钥”。
数据传输中记录层(Record layer)用于封装更高层的HTTP等协议。记录层数据可以被随意压缩、加密,与消息验证码压缩在一起。每个记录层包都有一个Content-Type段用以记录更上层用的协议。
使用SSL层接口函数有以下几个步骤:
1、初始化OpenSSL库
初始化函数列出如下:
#define OpenSSL_add_ssl_algorithms()SSL_library_init()
#define SSLeay_add_ssl_algorithms() SSL_library_init()
2、选择会话协议
客户端使用下面的函数选择会话协议:
const SSL_METHOD *SSLv2_client_method(void);/* SSLv2 */
const SSL_METHOD *SSLv3_client_method(void);/* SSLv3 */
const SSL_METHOD *SSLv23_client_method(void);/* SSLv3 but can rollback to v2 */
const SSL_METHOD *TLSv1_client_method(void);/* TLSv1.0 */
const SSL_METHOD *DTLSv1_client_method(void);/* DTLSv1.0 */
服务器端使用下面的函数选择会话协议:
const SSL_METHOD *SSLv2_server_method(void);/* SSLv2 */
const SSL_METHOD *SSLv3_server_method(void); /* SSLv3 */
const SSL_METHOD *SSLv23_server_method(void); /* SSLv3 but can rollback to v2 */
const SSL_METHOD *TLSv1_server_method(void); /* TLSv1.0 */
const SSL_METHOD *DTLSv1_server_method(void); /* DTLSv1.0 */
3、创建会话环境
创建会话环境:
SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
设置证书验证方式:
void SSL_CTX_set_verify(SSL_CTX *ctx,int mode,
int (*callback)(int, X509_STORE_CTX *));
给会话环境加载CA证书:
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_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_check_private_key(const SSL_CTX *ctx);
4、建立SSL套接字
SSL套接字建立在普通的TCP套接字基础上,应用程序在创建普通套接字、得到套接字描述符fd之后,再创建SSL套接字,并将fd绑定在SSL套接字上。
SSL *SSL_new(SSL_CTX *ctx);
intSSL_set_fd(SSL *s, int fd);
int SSL_set_rfd(SSL *s, int fd);
int SSL_set_wfd(SSL *s, int fd);
5、完成SSL握手
与普通socket编程类似,创建SSL套接字后,客户端使用SSL_connect替代普通socket的connect函数,服务器端则以SSL_accept代替普通socket的accept()函数。
int SSL_accept(SSL *ssl);
int SSL_connect(SSL *ssl);
握手完成之后,询问通信双方的证书信息:
X509 *SSL_get_peer_certificate(const SSL *s);
X509_NAME *X509_get_subject_name(X509 *a); // <openssl/x509.h>
6、数据传输
对数据的安全传输包括了加密/解密、压缩/解压缩的过程。
int SSL_read(SSL *ssl,void *buf,int num);
int SSL_peek(SSL *ssl,void *buf,int num);
int SSL_write(SSL *ssl,const void *buf,int num);
7、SSL通信结束
关闭SSL套接字、释放会话环境。
int SSL_shutdown(SSL *s);
voidSSL_free(SSL *ssl);
voidSSL_CTX_free(SSL_CTX *);
BIO是对IO操作的封装,OpenSSL的BIO抽象接口不仅可以对SSL连接的I/O使用,也可以对非加密的网络连接和文件的I/O使用。BIO的相关源代码在crypto/bio文件夹下。
BIO的相关数据结构列出如下。
BIO结构:
- struct bio_st
- {
- BIO_METHOD *method;
- /* bio, mode, argp, argi, argl, ret */
- long (*callback)(struct bio_st *,int,const char *,int, long,long);
- char *cb_arg; /* first argument for the callback */
- int init;
- int shutdown;
- int flags; /* extra storage */
- int retry_reason;
- int num;
- void *ptr;
- struct bio_st *next_bio; /* used by filter BIOs */
- struct bio_st *prev_bio; /* used by filter BIOs */
- int references;
- unsigned long num_read;
- unsigned long num_write;
- CRYPTO_EX_DATA ex_data;
- };
- typedef struct bio_method_st
- {
- int type;
- const char *name;
- int (*bwrite)(BIO *, const char *, int);
- int (*bread)(BIO *, char *, int);
- int (*bputs)(BIO *, const char *);
- int (*bgets)(BIO *, char *, int);
- long (*ctrl)(BIO *, int, long, void *);
- int (*create)(BIO *);
- int (*destroy)(BIO *);
- long (*callback_ctrl)(BIO *, int, bio_info_cb *);
- } BIO_METHOD;
- #define BIO_TYPE_DESCRIPTOR0x0100 /* socket, fd, connect or accept */
- #define BIO_TYPE_FILTER 0x0200
- #define BIO_TYPE_SOURCE_SINK 0x0400
1、源/接收类型
- #define BIO_TYPE_MEM(1|0x0400)
- #define BIO_TYPE_FILE (2|0x0400)
- #define BIO_TYPE_FD (4|0x0400|0x0100)
- #define BIO_TYPE_SOCKET (5|0x0400|0x0100)
- #define BIO_TYPE_NULL (6|0x0400)
- #define BIO_TYPE_CONNECT(12|0x0400|0x0100)/* socket - connect */
- #define BIO_TYPE_ACCEPT(13|0x0400|0x0100)/* socket for accept */
- #define BIO_TYPE_BIO(19|0x0400)/* (half a) BIO pair */
- #define BIO_TYPE_DGRAM(21|0x0400|0x0100)
2、过滤类型
- #define BIO_TYPE_SSL(7|0x0200)
- #define BIO_TYPE_MD(8|0x0200) /* passive filter */
- #define BIO_TYPE_BUFFER (9|0x0200)/* filter */
- #define BIO_TYPE_CIPHER (10|0x0200)/* filter */
- #define BIO_TYPE_BASE64 (11|0x0200)/* filter */
- #define BIO_TYPE_PROXY_CLIENT (14|0x0200)/* client proxy BIO */
- #define BIO_TYPE_PROXY_SERVER (15|0x0200)/* server proxy BIO */
- #define BIO_TYPE_NBIO_TEST (16|0x0200)/* server proxy BIO */
- #define BIO_TYPE_NULL_FILTER (17|0x0200)
- #define BIO_TYPE_BER (18|0x0200)/* BER -> bin filter */
- #define BIO_TYPE_LINEBUFFER (20|0x0200)/* filter */
- #define BIO_TYPE_ASN1 (22|0x0200)/* filter */
- #define BIO_TYPE_COMP (23|0x0200)/* filter */
BIO过滤缓冲结构:
- typedef struct bio_f_buffer_ctx_struct
- {
- /* BIO *bio; */ /* this is now in the BIO struct */
- int ibuf_size; /* how big is the input buffer */
- int obuf_size; /* how big is the output buffer */
- char *ibuf; /* the char array */
- int ibuf_len; /* how many bytes are in it */
- int ibuf_off; /* write/read offset */
- char *obuf; /* the char array */
- int obuf_len; /* how many bytes are in it */
- int obuf_off; /* write/read offset */
- } BIO_F_BUFFER_CTX;