msn: [email protected]
来源:http://yfydz.cublog.cn
2.6 SSL_CTX_set_default_passwd_cb[_userdata]() 这个函数比较简单,就是设置SSL要加载的证书的口令,如果不设置的话加载证书时会出提示符要求输入口令的,这样在程序中使用就比较麻烦,该函数就是预先将口令保存,在读证书时自动使用。 实现该功能的有两个函数SSL_CTX_set_default_passwd_cb()和SSL_CTX_set_default_passwd_cb_userdata(),前者是定义一个口令回调函数,要获取口令时口令由该函数获取;后者是直接将口令设置好。 /* ssl/ssl_lib.c */ void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) { ctx->default_passwd_callback=cb; } void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx,void *u) { ctx->default_passwd_callback_userdata=u; } 举例: static int pass_cb(char *buf, int len, int verify, void *password) { snprintf(buf,len, "123456"); return strlen(buf); } SSL_CTX_set_default_passwd_cb(ctx, pass_cb); 等价于: SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456"); 2.7 SSL_CTX_use_certificate_file() 该函数读取证书文件,证书文件通常都进行了加密保护。普及一下,证书文件里肯定是有公钥的,一般没私钥,某些情况会把私钥也包含进去,但那样作太不安全了,原则上私钥是永远不会给别人看到的,就算是进行了加密保护。 /* ssl/ssl_rsa.c */ int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { int j; BIO *in; int ret=0; X509 *x=NULL; in=BIO_new(BIO_s_file_internal()); if (in == NULL) { SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,ERR_R_BUF_LIB); goto end; } if (BIO_read_filename(in,file) <= 0) { SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB); goto end; } // 根据证书是PEM还是DER分别读取进行解码 // DER是二进制格式,PEM则是对DER用BASE64编码的后的文本格式 if (type == SSL_FILETYPE_ASN1) { j=ERR_R_ASN1_LIB; x=d2i_X509_bio(in,NULL); } else if (type == SSL_FILETYPE_PEM) { j=ERR_R_PEM_LIB; x=PEM_read_bio_X509(in,NULL,ssl->ctx->default_passwd_callback,ssl->ctx->default_passwd_callback_userdata); } else { SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE); goto end; } if (x == NULL) { SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,j); goto end; } // 加载解码后后的证书 ret=SSL_use_certificate(ssl,x); end: if (x != NULL) X509_free(x); if (in != NULL) BIO_free(in); return(ret); } 2.8 SSL_CTX_use_PrivateKey_file() 该函数加载私钥文件,和SSL_CTX_use_certificate_file()是类似的,因为RSA算法的公钥私钥是对称的,刚生成密钥时谁作私钥都行。 SSL_CTX_use_PrivateKey_file()只加载PEM格式私钥,DER格式的用函数SSL_use_PrivateKey_ASN1()加载。 /* ssl/ssl_rsa.c */ int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type) { int j,ret=0; BIO *in; EVP_PKEY *pkey=NULL; in=BIO_new(BIO_s_file_internal()); if (in == NULL) { SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_BUF_LIB); goto end; } if (BIO_read_filename(in,file) <= 0) { SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB); goto end; } // 私钥只支持PEM格式 if (type == SSL_FILETYPE_PEM) { j=ERR_R_PEM_LIB; pkey=PEM_read_bio_PrivateKey(in,NULL, ssl->ctx->default_passwd_callback,ssl->ctx->default_passwd_callback_userdata); } else { SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE); goto end; } if (pkey == NULL) { SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,j); goto end; } // 加载私钥 ret=SSL_use_PrivateKey(ssl,pkey); EVP_PKEY_free(pkey); end: if (in != NULL) BIO_free(in); return(ret); } 2.9 SSL_CTX_check_private_key() 该函数检查所用的公钥私钥是否是匹配的 int SSL_CTX_check_private_key(SSL_CTX *ctx) { if ( (ctx == NULL) || (ctx->cert == NULL) || (ctx->cert->key->x509 == NULL)) { SSLerr(SSL_F_SSL_CTX_CHECK_PRIVATE_KEY,SSL_R_NO_CERTIFICATE_ASSIGNED); return(0); } if (ctx->cert->key->privatekey == NULL) { SSLerr(SSL_F_SSL_CTX_CHECK_PRIVATE_KEY,SSL_R_NO_PRIVATE_KEY_ASSIGNED); return(0); } // 这才是真正比较函数,在crypto/x509/x509_cmp.c中定义 return(X509_check_private_key(ctx->cert->key->x509, ctx->cert->key->privatekey)); } 2.10 SSL_new 该函数根据SSL_CTX实现一个SSL结构实例,SSL结构是个很复杂的结构,定义如下: /* ssl/ssl.h */ typedef struct ssl_st SSL; struct ssl_st { /* protocol version * (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION) */ int version; int type; /* SSL_ST_CONNECT or SSL_ST_ACCEPT */ 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)(); /* 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 */ 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 */ int purpose; /* Purpose setting */ int trust; /* Trust setting */ /* 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 */ EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */ const EVP_MD *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 */ const EVP_MD *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_depth; 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 */ 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 */ }; /* ssl/ssl_lib.c */ SSL *SSL_new(SSL_CTX *ctx) { SSL *s; // 一些必要检查 if (ctx == NULL) { SSLerr(SSL_F_SSL_NEW,SSL_R_NULL_SSL_CTX); return(NULL); } if (ctx->method == NULL) { SSLerr(SSL_F_SSL_NEW,SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION); return(NULL); } // 分配SSL实例的空间 s=(SSL *)OPENSSL_malloc(sizeof(SSL)); if (s == NULL) goto err; memset(s,0,sizeof(SSL)); // 初始化SSL结构参数 #ifndef OPENSSL_NO_KRB5 s->kssl_ctx = kssl_ctx_new(); #endif /* OPENSSL_NO_KRB5 */ s->options=ctx->options; s->mode=ctx->mode; s->max_cert_list=ctx->max_cert_list; if (ctx->cert != NULL) { /* Earlier library versions used to copy the pointer to * the CERT, not its contents; only when setting new * parameters for the per-SSL copy, ssl_cert_new would be * called (and the direct reference to the per-SSL_CTX * settings would be lost, but those still were indirectly * accessed for various purposes, and for that reason they * used to be known as s->ctx->default_cert). * Now we don't look at the SSL_CTX's CERT after having * duplicated it once. */ s->cert = ssl_cert_dup(ctx->cert); if (s->cert == NULL) goto err; } else s->cert=NULL; /* Cannot really happen (see SSL_CTX_new) */ s->read_ahead=ctx->read_ahead; s->msg_callback=ctx->msg_callback; s->msg_callback_arg=ctx->msg_callback_arg; s->verify_mode=ctx->verify_mode; s->verify_depth=ctx->verify_depth; s->sid_ctx_length=ctx->sid_ctx_length; OPENSSL_assert(s->sid_ctx_length <= sizeof s->sid_ctx); memcpy(&s->sid_ctx,&ctx->sid_ctx,sizeof(s->sid_ctx)); s->verify_callback=ctx->default_verify_callback; s->generate_session_id=ctx->generate_session_id; s->purpose = ctx->purpose; s->trust = ctx->trust; s->quiet_shutdown=ctx->quiet_shutdown; CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX); s->ctx=ctx; s->verify_result=X509_V_OK; s->method=ctx->method; if (!s->method->ssl_new(s)) goto err; s->references=1; s->server=(ctx->method->ssl_accept == ssl_undefined_function)?0:1; SSL_clear(s); CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data); return(s); err: if (s != NULL) { if (s->cert != NULL) ssl_cert_free(s->cert); if (s->ctx != NULL) SSL_CTX_free(s->ctx); /* decrement reference count */ OPENSSL_free(s); } SSLerr(SSL_F_SSL_NEW,ERR_R_MALLOC_FAILURE); return(NULL); } 2.11 SSL_set_fd SSL_set_fd()函数将建立的SSL结构与TCP套接字联系,使SSL结构对套接字中的TCP数据进行SSL封装。 /* ssl/ssl_lib.c */ int SSL_set_fd(SSL *s,int fd) { int ret=0; BIO *bio=NULL; // 建立一个BIO,BIO是OpenSSL提供的用来进行算法封装的处理结构,还可以将多个算法 // 串联起来,这样可以很方便地实现数据的封装 bio=BIO_new(BIO_s_socket()); if (bio == NULL) { SSLerr(SSL_F_SSL_SET_FD,ERR_R_BUF_LIB); goto err; } // 把套接字和BIO联系 BIO_set_fd(bio,fd,BIO_NOCLOSE); // 把SSL和BIO联系起来,包括读写操作 SSL_set_bio(s,bio,bio); ret=1; err: return(ret); } void SSL_set_bio(SSL *s,BIO *rbio,BIO *wbio) { /* If the output buffering BIO is still in place, remove it */ if (s->bbio != NULL) { if (s->wbio == s->bbio) { s->wbio=s->wbio->next_bio; s->bbio->next_bio=NULL; } } if ((s->rbio != NULL) && (s->rbio != rbio)) BIO_free_all(s->rbio); if ((s->wbio != NULL) && (s->wbio != wbio) && (s->rbio != s->wbio)) BIO_free_all(s->wbio); // 设置SSL读写BIO s->rbio=rbio; s->wbio=wbio; } SSL_set_fd()还有两个类似函数: SSL_set_wfd():对写的数据进行SSL封装 SSL_set_rfd():对都的数据进行SSL封装 不过一般情况下用得比较少。 ...... 待续 ......