VS2012下64位的openssl的编译、配置、生成、C++代码及实验结果

可以转载,转载请注明出处,谢谢!

参考:

https://blog.csdn.net/qq_21111579/article/details/52134507 原理

https://www.linuxidc.com/Linux/2014-10/108502.htm 编译步骤

https://bbs.csdn.net/topics/392193545?page=1 (no-asm的坑)

https://blog.csdn.net/hpp24/article/details/54406970 生成证书(主要)代码(主要)

https://blog.csdn.net/oldmtn/article/details/52208747 代码(参考)

https://blog.csdn.net/gengxiaoming7/article/details/78505107 生成证书(参考client和server认证部分)

一、在win64下生成相应的库和汇编文件的编译步骤:

1.      将命令提示符定位到C:\OpenSSL\openssl-1.0.2o;

2.      输入 perl  Configure  debug-VC-WIN64A no-asm --perfix=C:\OpenSSL\openssl\win64 ;

3.      输入 ms\do_nasm ;

4.      输入 ms\do_win64a ;

5.      将命令提示符定位到C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\amd64 , 然后输入 vcvars64.bat;

6.      再次将命令提示符定位到C:\OpenSSL\openssl-1.0.2o;

7.    输入 nmake  -f  ms\ntdll.mak ;执行完后会在openssl-1.0.2o目录下生成out32dll.dbg和tmp32dll.dbg两个文件夹,out32dll.dbg文件夹中会生成两个动态库态库和dll等,tmp32文件夹中会生成相应的汇编文件;

8.      输入 nmake  -f  ms\ntdll.mak test ;若最终显示passed  all  tests说明生成的库正确;

9.      输入 nmake  -f  ms\ntdll.mak install ;则会在C:\usr\local\ssl目录下生成bin、include、lib、ssl四个文件夹;

10.    以上编译的是debug库,若编译release库,则将以上第2步中的debug-VC-WIN64A改成VC-WIN64A即可;

11.    若编译静态库则用ms\nt.mak替换掉上面用到的ms\ntdll.mak即可;

注意:

1.第二步要加上no-asm!!!原因:当你下载的openssl是高版本的话,如openssl-1.0.2o,需要在这里加上no-asm,表示不使用汇编,如果你不加上的话,在执行nmake命令时会出错。但是如果下载是openssl-1.0.1c,貌似不用加no-asm也可以成功。如果真的不想加no-asm,则需要在网上下一个。见https://bbs.csdn.net/topics/392193545?page=1 

2.出现nasm错误, 从http://sourceforge.net/projects/nasm/下载nasm 2.07,将其安装到D:\ProgramFiles\NASM中,并将D:\ProgramFiles\NASM添加到系统环境变量Path中

3. 最好电脑只有一个VS版本。多个版本加到环境变量在编译的时候容易出错。

二、证书、私钥、证书请求生成

生成证书:进入到C:\usr\local\ssl\bin,打开openssl.exe(必须右键,以管理员方式运行!)。

1.首先要生成服务器端的私钥(key文件):

genrsa -des3 -out server.key 1024

运行时会提示输入密码,此密码用于加密key文件(参数des3便是指加密算法,当然也可以选用其他你认为安全的算法.),以后每当需读取此文 件(通过openssl提供的命令或API)都需输入口令.如果觉得不方便,也可以去除这个口令,但一定要采取其他的保护措施!

去除key文件口令的命令:(这步我没有采用)

rsa -in server.key -out server.key

2.生成服务器请求

req -new -key server.key -out server.csr

生成Certificate Signing Request(CSR),生成的csr文件交给CA签名后形成服务端自己的证书.屏幕上将有提示,依照其指示一步一步输入要 求的个人信息即可.

3.对客户端也作同样的命令生成key及csr文件:

genrsa -des3 -out client.key 1024

req -new -key client.key -out client.csr

4.CSR文件必须有CA的签名才可形成证书.可将此文件发送到verisign等地方由它验证,要交一大笔钱,何不自己做CA呢.

首先生成CA的key文件:

genrsa -des3 -out ca.key 1024

在生成CA自签名的证书:

req -new -x509 -key ca.key -out ca.crt

如果想让此证书有个期限,如一年,则加上"-days 365".

5.用生成的CA的证书为刚才生成的server.csr,client.csr文件签名:

x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

最终bin目录下多出来了9个文件

三、测试代码

按正常VS工程来配置,include、lib和dll都在c:\usr\local\ssl文件夹下,把这些东西在VS工程的属性里面配好。然后把上面的证书、私钥等文件放到服务器和客户端工程的对应位置。

代码中加了#pragma comment( lib, "ws2_32.lib" )一行,因为不加会出链接问题。

参见https://bbs.csdn.net/topics/50453243

服务器:

/***************************************************************** 
*SSL/TLS服务端程序WIN32版(以demos/server.cpp为基础) 
*需要用到动态连接库libeay32.dll,ssleay.dll, 
*同时在setting中加入ws2_32.lib libeay32.lib ssleay32.lib, 
*所需证书文件请参照文章自行生成. 
*****************************************************************/  

#include <stdio.h>  
#include <stdlib.h>  
#include <memory.h>  
#include <errno.h>  
#include <sys/types.h>  

#include <winsock2.h>  

#include "openssl/rsa.h"        
#include "openssl/crypto.h"  
#include "openssl/x509.h"  
#include "openssl/pem.h"  
#include "openssl/ssl.h"  
#include "openssl/err.h"  
#pragma comment( lib, "ws2_32.lib" )
/*所有需要的参数信息都在此处以#define的形式提供*/  
#define CERTF   "server.crt" /*服务端的证书(需经CA签名)*/  
#define KEYF   "server.key"  /*服务端的私钥(建议加密存储)*/  
#define CACERT "ca.crt" /*CA 的证书*/  
#define PORT   1111   /*准备绑定的端口*/  

#define CHK_NULL(x) if ((x)==NULL) exit (1)  
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }  
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }  

int main ()  
{  
	int err;  
	int listen_sd;  
	int sd;  
	struct sockaddr_in sa_serv;  
	struct sockaddr_in sa_cli;  
	int client_len;  
	SSL_CTX* ctx;  
	SSL*     ssl;  
	X509*    client_cert;  
	char*    str;  
	char     buf [4096];  
	SSL_METHOD *meth;  
	WSADATA wsaData;  

	if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){  
		printf("WSAStartup()fail:%d\n",GetLastError());  
		return -1;  
	}  

	SSL_load_error_strings();            /*为打印调试信息作准备*/  
	OpenSSL_add_ssl_algorithms();        /*初始化*/  
	meth = const_cast<SSL_METHOD *>(TLSv1_server_method());  /*采用什么协议(SSLv2/SSLv3/TLSv1)在此指定*/  

	ctx = SSL_CTX_new (meth);  
	CHK_NULL(ctx);  

	//SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);   /*验证与否*/  
	// 服务器端不需要验证客户端的ca,改下就好了,见 https://bbs.csdn.net/topics/390222065
	// 如果选择SSL_VERIFY_PEER,则SSL_accept处会返回-1,一直报错
	SSL_CTX_set_verify(ctx,SSL_VERIFY_NONE,NULL);   /*验证与否*/  

	if(!SSL_CTX_load_verify_locations(ctx,CACERT,NULL)) /*若验证,则放置CA证书*/  
	{
		ERR_print_errors_fp(stderr);  
		exit(3);  
	}
	if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {  
		ERR_print_errors_fp(stderr);  
		exit(3);  
	}  
	if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {  
		ERR_print_errors_fp(stderr);  
		exit(4);  
	}  

	if (!SSL_CTX_check_private_key(ctx)) {  
		printf("Private key does not match the certificate public key\n");  
		exit(5);  
	}  

	SSL_CTX_set_cipher_list(ctx,"RC4-MD5");   

	/*开始正常的TCP socket过程.................................*/  
	printf("Begin TCP socket...\n");  

	listen_sd = socket (AF_INET, SOCK_STREAM, 0);    
	CHK_ERR(listen_sd, "socket");  

	memset (&sa_serv, '/0', sizeof(sa_serv));  
	sa_serv.sin_family      = AF_INET;  
	sa_serv.sin_addr.s_addr = INADDR_ANY;  
	sa_serv.sin_port        = htons (PORT);           

	err = bind(listen_sd, (struct sockaddr*) &sa_serv,  

		sizeof (sa_serv));  

	CHK_ERR(err, "bind");  

	/*接受TCP链接*/  
	err = listen (listen_sd, 5);                     
	CHK_ERR(err, "listen");  

	client_len = sizeof(sa_cli);  
	sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);  
	CHK_ERR(sd, "accept");  
	closesocket (listen_sd);  

	printf ("Connection from %lx, port %x\n",  
		sa_cli.sin_addr.s_addr, sa_cli.sin_port);  

	/*TCP连接已建立,进行服务端的SSL过程. */  
	printf("Begin server side SSL\n");  

	ssl = SSL_new (ctx);                            
	CHK_NULL(ssl);  
	SSL_set_fd (ssl, sd);  
	err = SSL_accept (ssl);  
	printf("SSL_accept finished\n"); 
	if (err == -1) {
		int n2 = SSL_get_error(ssl, err);
		const char* p1 = SSL_state_string(ssl);
		const char* p2 = SSL_state_string_long( ssl );
		int a = 2;
		int b = a;
	}
	CHK_SSL(err);  

	/*打印所有加密算法的信息(可选)*/  
	printf ("SSL connection using %s\n", SSL_get_cipher (ssl));  

	/*这一段不要采用,因为前面设置的是SSL_VERIFY_NONE,服务器不读取客户端的证书。*/  
	client_cert = SSL_get_peer_certificate (ssl);  
	if (client_cert != NULL) {  
		printf ("Client certificate:\n");  

		str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);  
		CHK_NULL(str);  
		printf ("\t subject: %s\n", str);  
		free (str);  

		str = X509_NAME_oneline (X509_get_issuer_name  (client_cert), 0, 0);  
		CHK_NULL(str);  
		printf ("\t issuer: %s\n", str);  
		free (str);  

		X509_free (client_cert);/*如不再需要,需将证书释放 */  
	}  
	else  
		printf ("Client does not have certificate.\n");  
	
	
	/* 数据交换开始,用SSL_write,SSL_read代替write,read */  
	while (true)
	{
		
		err = SSL_read (ssl, buf, sizeof(buf) - 1);                    
		CHK_SSL(err);  
		buf[err] = '\0';  
		printf ("Got %d chars:'%s'\n", err, buf);  

		err = SSL_write (ssl, "I hear you.", strlen("I hear you."));   
		CHK_SSL(err);  
		Sleep(500);
	}

	/* 收尾工作*/  
	shutdown (sd,2);  
	SSL_free (ssl);  
	SSL_CTX_free (ctx);  

	system("pause");
	return 0;  
}  
/***************************************************************** 
* EOF - serv.cpp 
*****************************************************************/  

客户端:

/***************************************************************** 
*SSL/TLS客户端程序WIN32版(以demos/cli.cpp为基础) 
*需要用到动态连接库libeay32.dll,ssleay.dll, 
*同时在setting中加入ws2_32.lib libeay32.lib ssleay32.lib, 
*所需证书文件请参照文章自行生成*/  
/******************************************************************/  
#include <stdio.h>  
#include <stdlib.h>  
#include <memory.h>  
#include <errno.h>  
#include <sys/types.h>  

#include <winsock2.h>  

#include "openssl/rsa.h"        
#include "openssl/crypto.h"  
#include "openssl/x509.h"  
#include "openssl/pem.h"  
#include "openssl/ssl.h"  
#include "openssl/err.h"  
#include "openssl/rand.h"  


#pragma comment( lib, "ws2_32.lib" )
/*所有需要的参数信息都在此处以#define的形式提供*/  
#define CERTF  "client.crt"  /*客户端的证书(需经CA签名)*/  
#define KEYF  "client.key"   /*客户端的私钥(建议加密存储)*/  
#define CACERT "ca.crt"      /*CA 的证书*/  
#define PORT   1111          /*服务端的端口*/  
#define SERVER_ADDR "127.0.0.1"  /*服务段的IP地址*/  

#define CHK_NULL(x) if ((x)==NULL) exit (-1)  
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(-2); }  
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(-3); }  

int main ()  
{  
	int err;  
	int sd;  
	struct sockaddr_in sa;  
	SSL_CTX* ctx;  
	SSL*     ssl;  
	X509*    server_cert;  
	char*    str = new char[1000];  
	char     buf [4096];  
	SSL_METHOD *meth;  
	int       seed_int[100]; /*存放随机序列*/  

	WSADATA wsaData;  

	if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){  
		printf("WSAStartup()fail:%d/n",GetLastError());  
		return -1;  
	}   

	OpenSSL_add_ssl_algorithms(); /*初始化*/  
	SSL_load_error_strings();     /*为打印调试信息作准备*/  
	SSLeay_add_ssl_algorithms();
	ERR_load_BIO_strings();

	meth = const_cast<SSL_METHOD*>(TLSv1_client_method()); /*采用什么协议(SSLv2/SSLv3/TLSv1)在此指定*/  
	ctx = SSL_CTX_new (meth);                         
	CHK_NULL(ctx);  

	SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);   /*验证与否*/  
	if(!SSL_CTX_load_verify_locations(ctx,CACERT,NULL)) /*若验证,则放置CA证书*/  
	{
		ERR_print_errors_fp(stderr);  
		exit(3);  
	}

	if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {  
		ERR_print_errors_fp(stderr);  
		exit(-2);  
	}  
	if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {  
		ERR_print_errors_fp(stderr);  
		exit(-3);  
	}  

	if (!SSL_CTX_check_private_key(ctx)) {  
		printf("Private key does not match the certificate public key/n");  
		exit(-4);  
	}   

	/*构建随机数生成机制,WIN32平台必需*/  
/*
	srand( (unsigned)time( NULL ) );  
	for( int i = 0;   i < 100;i++ )  
		seed_int[i] = rand();  
	RAND_seed(seed_int, sizeof(seed_int));  */

	/*以下是正常的TCP socket建立过程 .............................. */  
	printf("Begin tcp socket...\n");  

	sd = socket (AF_INET, SOCK_STREAM, 0);       CHK_ERR(sd, "socket");  

	memset (&sa, '\0', sizeof(sa));  
	sa.sin_family      = AF_INET;  
	sa.sin_addr.s_addr = inet_addr (SERVER_ADDR);   /* Server IP */  
	sa.sin_port        = htons     (PORT);          /* Server Port number */  

	err = connect(sd, (struct sockaddr*) &sa,  
		sizeof(sa));   
	CHK_ERR(err, "connect");  

	/* TCP 链接已建立.开始 SSL 握手过程.......................... */  
	printf("Begin SSL negotiation \n");  

	ssl = SSL_new (ctx);                          
	CHK_NULL(ssl);  

	SSL_set_fd (ssl, sd);  
	err = SSL_connect (ssl); 

	if (err == -1) {
		int n2 = SSL_get_error(ssl, err);
		const char* p1 = SSL_state_string(ssl);
		const char* p2 = SSL_state_string_long( ssl );
		int a = 2;
		int b = a;
	}

	CHK_SSL(err);  

	/*打印所有加密算法的信息(可选)*/  
	printf ("SSL connection using %s\n", SSL_get_cipher (ssl));  

	/*得到服务端的证书并打印些信息(可选) */  
	server_cert = SSL_get_peer_certificate (ssl);        
	CHK_NULL(server_cert);  
	printf ("Server certificate:\n");  

	str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);  
	CHK_NULL(str);  
	printf ("\t subject: %s\n", str);  
	//free (str);  

	str = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0);  
	CHK_NULL(str);  
	printf ("\t issuer: %s\n", str);  
	//free (str);  

	X509_free (server_cert);  /*如不再需要,需将证书释放 */  
	
	/* 数据交换开始,用SSL_write,SSL_read代替write,read */  
	printf("Begin SSL data exchange\n");  
	while (true)
	{
		err = SSL_write (ssl, "Hello World!", strlen("Hello World!"));   
		CHK_SSL(err);  

		err = SSL_read (ssl, buf, sizeof(buf) - 1);   
		CHK_SSL(err);  

		buf[err] = '\0';  
		printf ("Got %d chars:'%s'\n", err, buf);  
		Sleep(00);

	}

	SSL_shutdown (ssl);  /* send SSL/TLS close_notify */  

	/* 收尾工作 */  
	shutdown (sd,2);  
	SSL_free (ssl);  
	SSL_CTX_free (ctx);  
	system("pause");

	return 0;  
}  
/***************************************************************** 
* EOF - cli.cpp 
*****************************************************************/  

 四、实验结果

在运行的刚开始,客户端和服务器的服务器私钥密码都是输入123456(这个密码是在创建密钥的时候自己设置的,可以参考网上其他人的博文),然后回车:

猜你喜欢

转载自blog.csdn.net/zz420521/article/details/81188447