SSL建立連接


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封裝 不過一般情況下用得比較少。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM