概述
本文主要讲到了openssl的基本使用方法,开发环境为windows,开发工具为VS2019.本文主要是说明openssl如何使用,不介绍任何理论知识,如果有不懂的,请自行百度。个人建议下一个everything查询工具,真的很好用,比window自带的查询快了很多,可以查询自己想要的文件
OPENSSL安装
安装过程网上有很多,OPENSSL安装,注意你安装的OPENSSL的版本以及位数(32位或者64位),假如我安装的是64位的openssl,安装目录为D:\Program Files\OpenSSL-Win64,你可以自行选择你的安装目录,安装完成后,查看安装的openssl版本,使用控制台输入openssl version即可
秘钥key和公钥的生成
在控制命令行中输入以下命令:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365
网上有很多生成秘钥和公钥的文章,不过都没成功,最后在stackoverflow找到了可以用的方法,原文地址为:openssl秘钥和公钥的生成,以下是截图
Enter PEM pass phrase:提示输入pem文件的密码,注意,后面要用到这个密码,可以使用自己常用的密码,输入后会再次确认密码,然后就是一些基本信息,可以默认为空。截图如下,基本上这时候就得到了秘钥key.pem和公钥cert.pem,后面要用到这2个文件。,生成的位置为当前目录,比如我的就是在C:\Users\86138,即控制台的显示目录。
项目设置
使用VS创建一个控制台项目,创建一个客户端和服务器项目,由于我下载的是64位,openssl3.0版本。因此我项目也是64位的。
项目的配置,服务器端和客户端都是相同配置,这里我就只说一个即可,主要是设置openssl的lib和头文件的路径,这和使用任外部库都是一样的。截图如下,注意我的openssl安装位置为D:\Program Files\OpenSSL-Win64,请选择你安装位置即可。
预处理器里添加2个定义:CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;
附加依赖项:libssl.lib;openssl.lib;libcrypto.lib;liblegacy.lib;
最后将前面生成的cert.pem和key.pem放到exe目录下,为什么放到这个目录,是因为下面的代码用到的这2个文件是当前目录,不管怎么样,只要能找到这2个文件即可。
配置很简单,就上面几步,基本上就可以了。
代码部分
客户端代码
#include <stdio.h> #include <errno.h> #include <malloc.h> #include <string.h> # include <winsock2.h> #include <ws2tcpip.h> #include <openssl/ssl.h> #include <openssl/err.h> #define FAIL -1 #pragma comment(lib, "Ws2_32.lib") //Added the LoadCertificates how in the server-side makes. int OpenConnection(const char* hostname, int port) { SOCKET sd; struct hostent* host; struct sockaddr_in addr; WORD wVersionRequested; WSADATA wsaData; int err; /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { /* Tell the user that we could not find a usable */ /* Winsock DLL. */ printf("WSAStartup failed with error: %d\n", err); return 1; } sd = socket(PF_INET, SOCK_STREAM, 0); /bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(hostname); int ret = connect(sd, (struct sockaddr*)&addr, sizeof(addr)); if ( ret != 0) { //close(sd); perror(hostname); abort(); } return sd; } SSL_CTX* InitCTX(void) { //SSL_METHOD* method; SSL_CTX* ctx; //OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */ SSL_load_error_strings(); /* Bring in and register error messages */ SSL_METHOD const* meth = SSLv23_client_method(); /* Create new client-method instance */ //method = TLSv1_method(); ctx = SSL_CTX_new(meth); /* Create new context */ if (ctx == NULL) { ERR_print_errors_fp(stderr); printf("Eroor: %s\n", stderr); abort(); } return ctx; } void ShowCerts(SSL* ssl) { X509* cert; char* line1, * line2; cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */ if (cert != NULL) { printf("Server certificates:\n"); line1 = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); printf("Subject: %s\n", line1); //free(line); /* free the malloc'ed string */ line2 = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); printf("Issuer: %s\n", line2); //free(line); /* free the malloc'ed string */ //X509_free(cert); /* free the malloc'ed certificate copy */ } else printf("No certificates.\n"); } int main(int count, char* strings[]) { SSL_CTX* ctx; SOCKET server; SSL* ssl; char buf[1024]; int bytes; char const *hostname, *portnum; SSL_library_init(); hostname = "127.0.0.1"; portnum = "1030"; ctx = InitCTX(); server = OpenConnection(hostname, atoi(portnum)); ssl = SSL_new(ctx); /* create new SSL connection state */ //SSL_set_fd(ssl, server); /* attach the socket descriptor */ BIO* bio = BIO_new_socket(server, BIO_NOCLOSE); SSL_set_bio(ssl, bio, bio); SSL_set_connect_state(ssl); if (SSL_connect(ssl) == FAIL) /* perform the connection */ { printf("Eroor: %s\n", stderr); ERR_print_errors_fp(stderr); } else { printf("Connected with %s encryption\n", SSL_get_cipher(ssl)); ShowCerts(ssl); /* get any certs */ while (1) { char p[100]; printf("please input sned msg: "); gets(p); printf("strlen:%d,%s\n", strlen(p), p); SSL_write(ssl, p, strlen(p)); /* encrypt & send message */ bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */ if (bytes >= 0) { buf[bytes] = '\0'; printf("Received: \"%s\"\n", buf); } else { SSL_ERROR_WANT_READ; int error = SSL_get_error(ssl, bytes); printf("error:%d\n", error); break; } } SSL_shutdown(ssl); SSL_free(ssl); /* release connection state */ } SSL_CTX_free(ctx); /* release context */ getchar(); return 0; }
服务器端代码
#include <errno.h> #include <malloc.h> #include <string.h> # include <winsock2.h> # include <ws2tcpip.h> #include "openssl/ssl.h" #include "openssl/err.h" #ifdef __cplusplus extern "C" { #endif #include "openssl/applink.c" #ifdef __cplusplus } #endif #define FAIL -1 #pragma comment(lib, "Ws2_32.lib") int OpenListener(WORD port) { SOCKET m_socket; WORD wVersionRequested; WSADATA wsaData; int err; /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { /* Tell the user that we could not find a usable */ /* Winsock DLL. */ printf("WSAStartup failed with error: %d\n", err); return 1; } m_socket = socket(AF_INET, SOCK_STREAM, 0); if (m_socket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); WSACleanup(); return 0; } struct sockaddr_in sain; //bzero(&addr, sizeof(addr)); sain.sin_family = AF_INET; sain.sin_port = htons(port); sain.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(m_socket, (struct sockaddr*)&sain, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { perror("can't bind port"); //abort(); } if (listen(m_socket, 10) != 0) { perror("Can't configure listening port"); //abort(); } return m_socket; } SSL_CTX* InitServerCTX(void) { SSL_CTX* ctx = NULL; #if OPENSSL_VERSION_NUMBER >= 0x10000000L const SSL_METHOD* method; #else SSL_METHOD* method; #endif SSL_library_init(); //OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */ SSL_load_error_strings(); /* load all error messages */ //method = SSLv23_method(); /* create new server-method instance */ method = SSLv23_server_method(); ctx = SSL_CTX_new(method); /* create new context from method */ if (ctx == NULL) { ERR_print_errors_fp(stderr); abort(); } return ctx; } void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile) { //New lines int ret = SSL_CTX_load_verify_locations(ctx, CertFile, KeyFile); if (ret != 1) ERR_print_errors_fp(stderr); if (SSL_CTX_set_default_verify_paths(ctx) != 1) ERR_print_errors_fp(stderr); //End new lines /* set the local certificate from CertFile */ if (SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); //abort(); } /* set the private key from KeyFile (may be the same as CertFile) */ if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); abort(); } /* verify private key */ if (!SSL_CTX_check_private_key(ctx)) { fprintf(stderr, "Private key does not match the public certificate\n"); abort(); } printf("LoadCertificates success\n"); } void ShowCerts(SSL* ssl) { X509* cert; char* line; cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */ if (cert != NULL) { printf("Server certificates:\n"); line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); printf("Subject: %s\n", line); free(line); line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); printf("Issuer: %s\n", line); free(line); X509_free(cert); } else printf("No certificates.\n"); } void Servlet(SSL* ssl, SOCKET client) /* Serve the connection -- threadable */ { char buf[1024]; char reply[1024]; int sd, bytes; const char* HTMLecho = "hello client"; ShowCerts(ssl); /* get any certificates */ int ret = SSL_accept(ssl); while (1) { //bytes = recv(client, buf, sizeof(buf), 0); bytes = SSL_read(ssl, buf, sizeof(buf)); if (bytes > 0) { buf[bytes] = '\0'; printf("Client msg: %s\n", buf); sprintf(reply, HTMLecho, buf); /* construct reply */ SSL_write(ssl, reply, strlen(reply)); /* send reply */ } else //其他情况 { //printf("read byte < 0\n"); } } SSL_shutdown(ssl); SSL_free(ssl); } int main(int count, char* strings[]) { SSL_CTX* ctx; SOCKET server; char* portnum; server = OpenListener(1030); /* create server socket */ SSL_library_init(); portnum = strings[1]; ctx = InitServerCTX(); /* initialize SSL */ LoadCertificates(ctx, (char *)"cert.pem", (char *)"key.pem"); /* load certs */ while (1) { struct sockaddr_in addr; socklen_t len = sizeof(addr); SSL* ssl; SOCKET client = accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */ socklen_t len1; struct sockaddr_storage addr1; //add1.sin_family = AF_INET; char ipstr[INET6_ADDRSTRLEN]; int port; len1 = sizeof addr; int r; r = getpeername(client, (struct sockaddr*)&addr1, &len1); // deal with both IPv4 and IPv6: if (addr1.ss_family == AF_INET) { struct sockaddr_in* s = (struct sockaddr_in*)&addr1; port = ntohs(s->sin_port); inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); } else { // AF_INET6 struct sockaddr_in6* s = (struct sockaddr_in6*)&addr1; port = ntohs(s->sin6_port); inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); } printf("Peer IP address: %s,port:%d\n", ipstr,port); ssl = SSL_new(ctx); /* get new SSL state with context */ //SSL_set_fd(ssl, client); /* set connection socket to SSL state */ BIO* bio = BIO_new_socket(client, BIO_NOCLOSE); SSL_set_bio(ssl, bio, bio); Servlet(ssl, client); /* service connection */ } SSL_CTX_free(ctx); /* release context */ }
总结
可能遇到的问题
1.要确保代码中applink.c文件的存在,否则服务器端代码会提示Unlink的错误
2.服务器端代码要求输入密码,请使用生成秘钥时的密码