OpenSSL編程的基本步驟

OpenSSL編程的基本步驟 


===================================== 啓用加密 ======================================


		客戶端必備過程:
		
		0. 變量定義		
			BIO     *conn;		/*底層socket連接*/
	    SSL     *ssl;			/*SSL連接*/
	    SSL_CTX *ctx;			/*SSL會話、上下文*/
		
		
		1. 初始化
		
			SSL_library_init() 或者  OpenSSL_add_ssl_algorithms()
				/*SSL_library_init() registers the available SSL/TLS ciphers and digests.*/
				
			SSL_load_error_strings();
			
			ctx = SSL_CTX_new(SSLv23_method(  ));
				/*
				SSL_CTX對象將是產生SSL連接對象的工廠,是上下文的聯繫。 這個ssl的上下文使我們可以在建立
				連接之前設置連接配置參數,例如協議版本,證書信息和驗證要求。
				SSL_CTX_new() creates a new SSL_CTX object as framework to establish TLS/SSL or DTLS enabled 
				connections. An SSL_CTX object is reference counted.
				*/
			
			SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR);
				/*
				set default locations for trusted CA certificates
				將受信任的證書正確加載到SSL_CTX對象中,OpenSSL就具有一個內置功能來自動驗證對等方的證書鏈
				*/
			
			SSL_CTX_set_default_verify_paths(ctx);
				/*
				可選
				指定應使用加載CA證書的默認位置
				*/
			
			SSL_CTX_use_certificate_chain_file(ctx, CERTFILE);
				/* 
				加載信任的證書鏈,SSL_use_certificate_chain_file()也可以
				SSL_CTX_use_certificate_chain_file() loads a certificate chain from file into ctx. The certificates 
				must be in PEM format and must be sorted starting with the subject's certificate (actual client or 
				server certificate), followed by intermediate CA certificates if applicable, and ending at the 
				highest level (root) CA. SSL_use_certificate_chain_file() is similar except it loads the certificate
				chain into ssl.
				*/
				
			SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM);
				/*
				加載與證書中嵌入的公共密鑰相對應私鑰文件
				SSL_CTX_use_PrivateKey_file() adds the first private key found in file to ctx. The formatting type 
				of the private key must be specified from the known types SSL_FILETYPE_PEM, SSL_FILETYPE_ASN1
				*/
			
			/*
			SSL_CTX_set_default_passwd_cb()
				sets the default password callback called when loading/storing a PEM certificate with encryption.
			*/
			
			SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback/*下面舉例了*/);
				/*
				控制握手期間如何處理證書和請求:SSL_VERIFY_PEER
				*/
			
			
    	SSL_CTX_set_verify_depth(ctx, 4);
    		/*
    		證書驗證深度
    		*/
    	
    	SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
    	
    	SSL_CTX_set_cipher_list(ctx, CIPHER_LIST); /* #define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" */
    		/*
    		設置可用的密碼套件
    		*/		
		
		2. 連接建立				
			
			conn = BIO_new_connect(SERVER ":" PORT);
				/*
				創建一個新的BIO連接
				*/
				
			BIO_do_connect(conn);
				/*
				BIO_do_connect()嘗試連接提供的BIO。
				*/
			
			ssl = SSL_new(ctx);
				/*
				創建SSL類型對象,處理TLS/SSL連接。ssl繼承底層ctx中的信息:connection method, options, 
				verification settings, timeout settings。An SSL structure is reference counted
				*/
				
			
			SSL_set_bio(ssl, conn, conn);
				/*
				connect the SSL object with a BIO
				*/
			
			SSL_connect(ssl);
				/*
				initiate the TLS/SSL handshake with an TLS/SSL server。
				輔助函數BIO_set_tcp_ndelay()可用於打開或關閉TCP_NODELAY選項。
				*/
			
			post_connection_check(ssl, SERVER); /*一些驗證,後面舉例了*/
			
			SSL_write(ssl, buf + nwritten, sizeof(buf) - nwritten);
				/*  數據讀寫
				If necessary, a write function will negotiate a TLS/SSL session, if not already explicitly performed by
        SSL_connect(3) or SSL_accept(3). If the peer requests a re-negotiation, it will be performed
        transparently during the write function operation. The behaviour of the write functions depends on the
        underlying BIO.
       
        有阻塞和非阻塞兩種情況。non-blocking  blocking
       */
		
			SSL_shutdown(ssl);
				/*
				SSL_shutdown()關閉活動的TLS / SSL連接。它將close_notify關閉警報發送給對等方。
				SSL_shutdown()僅關閉寫入方向。調用SSL_shutdown()後無法調用SSL_write()。讀取方向被對等方關閉。
				*/
			
			SSL_clear(ssl);
				/*
				重置SSL對象以允許另一個連接。如果會話仍處於打開狀態,則將其視爲錯誤會話,並將按照RFC2246
				的要求將其從會話緩存中刪除。
				*/
			
		3. 資源釋放
			SSL_free(ssl);
			
			SSL_CTX_free(ctx);
		
			/*BIO_free(conn); 無需使用*/
		
		
		
--------------------------------------------------------------------
		服務端必備過程:
		
		0. 變量定義
			BIO         *acc, *client;
			SSL         *ssl;
    	SSL_CTX     *ctx;
		
		1. 初始化
		
			SSL_library_init() 或者  OpenSSL_add_ssl_algorithms()
				/*SSL_library_init() registers the available SSL/TLS ciphers and digests.*/
			
			SSL_load_error_strings();
			
			ctx = SSL_CTX_new(SSLv23_method());
			
			SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR)
			
			SSL_CTX_set_default_verify_paths(ctx) 
			
			SSL_CTX_use_certificate_chain_file(ctx, CERTFILE);
			
			SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM);
			
			SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
			
			SSL_CTX_set_verify_depth(ctx, 4);
			
			SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE);
			
			SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback);
			
			SSL_CTX_set_cipher_list(ctx, CIPHER_LIST);
    
			
		2. 連接建立	
    	
    	acc = BIO_new_accept(PORT);
			BIO_do_accept(acc);
			BIO_do_accept(acc);
				/*BIO_do_accept() serves two functions. When it is first called, after the accept BIO has been setup, it
		       will attempt to create the accept socket and bind an address to it. Second and subsequent calls to
		       BIO_do_accept() will await an incoming connection, or request a retry in non blocking mode.*/
			
			client = BIO_pop(acc);

			ssl = SSL_new(ctx);
				
			SSL_set_bio(ssl, client, client);
			
			SSL_set_accept_state(ssl);
			
			SSL_accept(ssl);
			
			post_connection_check(ssl, CLIENT);
			
			SSL_read(ssl, buf + nread, sizeof(buf) - nread);
			
			(SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) ? 1 : 0;
				/*
				判斷是否收到了對端的shutdown,客戶端也應該添加這條
				*/
			
			SSL_shutdown(ssl);
			
			SSL_clear(ssl);
			
			ERR_remove_state(0);
				/*
					void ERR_remove_state(unsigned long tid);
					frees the error queue associated with the specified thread, identified by tid
				*/
		
		3. 資源釋放
			SSL_free(ssl);
			
			SSL_CTX_free(ctx);
			
	
=================================二、 其它特性=================================
	1. 保存session
		SSL_set_session(ssl, saved session);
		SSL_get1_session(ssl);
	
		 保存session到磁盤
		int new_session_cb(SSL *ctx, SSL_SESSION *session); 
		void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *session); 
		SSL_SESSION *get_session_cb(SSL *ctx, unsigned char *id, int len, int *ref); 
		
	2. 同步/異步消息讀取
	
	3. 會話重新協商(更新對話祕鑰)
		SSL_renegotiate(ssl); 


=================================三、一些證書驗證的函數、回調函數=================================
	int verify_callback(int ok, X509_STORE_CTX *store)
	{
	    char data[256];
	 
	    if (!ok)
	    {
	        X509 *cert = X509_STORE_CTX_get_current_cert(store);
	        int  depth = X509_STORE_CTX_get_error_depth(store);
	        int  err = X509_STORE_CTX_get_error(store);
	 
	        fprintf(stderr, "-Error with certificate at depth: %i\n", depth);
	        X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
	        fprintf(stderr, "  issuer   = %s\n", data);
	        X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
	        fprintf(stderr, "  subject  = %s\n", data);
	        fprintf(stderr, "  err %i:%s\n", err, X509_verify_cert_error_string(err));
	    }
	 
	    return ok;
	}

	long post_connection_check(SSL *ssl, char *host)
	{
	    X509      *cert;
	    X509_NAME *subj;
	    char      data[256];
	    int       extcount;
	    int       ok = 0;
	 
	    /* Checking the return from SSL_get_peer_certificate here is not strictly
	     * necessary.  With our example programs, it is not possible for it to return
	     * NULL.  However, it is good form to check the return since it can return NULL
	     * if the examples are modified to enable anonymous ciphers or for the server
	     * to not require a client certificate.
	     */
	    if (!(cert = SSL_get_peer_certificate(ssl)) || !host)
	        goto err_occured;
	    if ((extcount = X509_get_ext_count(cert)) > 0)
	    {
	        int i;
	 
	        for (i = 0;  i < extcount;  i++)
	        {
	            char              *extstr;
	            X509_EXTENSION    *ext;
	 
	            ext = X509_get_ext(cert, i);
	            extstr = (char*) OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
	 
	            if (!strcmp(extstr, "subjectAltName"))
	            {
	                int                  j;
	                unsigned char        *data;
	                STACK_OF(CONF_VALUE) *val;
	                CONF_VALUE           *nval;
	                X509V3_EXT_METHOD    *meth;
	                void                 *ext_str = NULL;
	 
	                if (!(meth = X509V3_EXT_get(ext)))
	                    break;
	                data = ext->value->data;

	#if (OPENSSL_VERSION_NUMBER > 0x00907000L)
	                if (meth->it)
	                  ext_str = ASN1_item_d2i(NULL, &data, ext->value->length,
	                                          ASN1_ITEM_ptr(meth->it));
	                else
	                  ext_str = meth->d2i(NULL, &data, ext->value->length);
	#else
	                ext_str = meth->d2i(NULL, &data, ext->value->length);
	#endif
	                val = meth->i2v(meth, ext_str, NULL);
	                for (j = 0;  j < sk_CONF_VALUE_num(val);  j++)
	                {
	                    nval = sk_CONF_VALUE_value(val, j);
	                    if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host))
	                    {
	                        ok = 1;
	                        break;
	                    }
	                }
	            }
	            if (ok)
	                break;
	        }
	    }
	 
	    if (!ok && (subj = X509_get_subject_name(cert)) &&
	        X509_NAME_get_text_by_NID(subj, NID_commonName, data, 256) > 0)
	    {
	        data[255] = 0;
	        if (strcasecmp(data, host) != 0)
	            goto err_occured;
	    }
	 
	    X509_free(cert);
	    return SSL_get_verify_result(ssl);
	 
	err_occured:
	    if (cert)
	        X509_free(cert);
	    return X509_V_ERR_APPLICATION_VERIFICATION;
	}
	
	void init_dhparams(void)
	{
	    BIO *bio;

	    bio = BIO_new_file("dh512.pem", "r");
	    if (!bio)
	        int_error("Error opening file dh512.pem");
	    dh512 = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
	    if (!dh512)
	        int_error("Error reading DH parameters from dh512.pem");
	    BIO_free(bio);

	    bio = BIO_new_file("dh1024.pem", "r");
	    if (!bio)
	        int_error("Error opening file dh1024.pem");
	    dh1024 = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
	    if (!dh1024)
	        int_error("Error reading DH parameters from dh1024.pem");
	    BIO_free(bio);
	}

	DH *tmp_dh_callback(SSL *ssl, int is_export, int keylength)
	{
	    DH *ret;

	    if (!dh512 || !dh1024)
	        init_dhparams(  );

	    switch (keylength)
	    {
	        case 512:
	            ret = dh512;
	            break;
	        case 1024:
	        default: /* generating DH params is too costly to do on the fly */
	            ret = dh1024;
	            break;
	    }
	    return ret;
	}

	#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章