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"

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值