First, background
1.1 Problems
A recent product inspection report recommended the use of PKI-based authentication. Since the product already implements HTTPS, it was assumed that this meant the use of two-way authentication to deal with man-in-the-middle attacks.
“Information Security Engineering” in contact with two-way authentication, but there are two problems.
The first one was the final course design at that time. The client was a browser, and the server was Tomcat two-way authentication. You only needed to configure both of them without actually implementing the code yourself.
The second is that although the course also has close to bidirectional authentication implementation code, but at that time Java+JCE environment is now using C++ OpenSSL environment, the general meaning is still similar but the specific functions and parameters are quite different.
So what we have now is: the idea of certificate generation + the idea of bidirectional authentication implementation. For readers, it is assumed that they have a basic understanding of certificates, SSL/TSL, socket programming and other concepts, which will not be covered in detail in this article.
Based on this, the problem to be solved in this paper is: how openSSL generates the certificate and how OpenSSL implements bidirectional authentication.
1.2 Solutions
1.2.1 How to Generate an OpenSSL Certificate
Reference blog.csdn.net/gengxiaomin… Plus some other articles
1.2.2 OpenSSL Bidirectional Authentication Solution
Using blog.csdn.net/sardden/art… Code SSL, on the basis of the realization of two-way authentication.
The key to bidirectional authentication is the following functions (both server-side and client-side), and the rest will not be discussed in detail in the code comments:
SSL_CTX_set_verify—- Enable bidirectional authentication
SSL_CTX_load_verify_locations—- Loads the trusted root certificate
SSL_CTX_use_certificate_file—- Loads the certificate
SSL_CTX_use_PrivateKey_file—- Loads the private key
SSL_get_verify_result—- To verify, this function must be called otherwise the previous four optical configurations will not authenticate either way
Two, the implementation of two-way authentication procedures
2.1 Installing OpenSSL and development apis
apt-get install libssl-dev
Copy the code
2.2 Server code
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>#include <arpa/inet.h> #include <openssl/ssl.h> #include <openssl/err.h> #define MAXBUF 1024 void ShowCerts(SSL * ssl) { X509 *cert; char *line; cert = SSL_get_peer_certificate(ssl); SSL_CTX_set_verify(); // If this function is not enabled, the certificate will be authenticated. If (SSL_get_verify_result(SSL) == X509_V_OK){printf(" Certificate verified \n"); } if (cert ! = NULL) {printf(" Digital certificate info :\n"); line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); Printf (" certificate: %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 certificate info! \n"); } int main(int argc, char **argv) { int sockfd, new_fd; socklen_t len; struct sockaddr_in my_addr, their_addr; unsigned int myport, lisnum; char buf[MAXBUF + 1]; SSL_CTX *ctx; if (argv[1]) myport = atoi(argv[1]); else myport = 7838; if (argv[2]) lisnum = atoi(argv[2]); else lisnum = 2; SSL_library_init(); /* SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); /* Load all SSL error messages */ SSL_load_error_strings(); SSL_CTX = SSL_CTX_new(SSLv23_server_method()); /* SSL_CTX = SSL_CTX_new(SSLv23_server_method()); SSLv2_server_method() or SSLv3_server_method() can also be used to represent the V2 or V3 standards alone */ if (CTX == NULL) {ERR_print_errors_fp(stdout); exit(1); SSL_VERIFY_PEER-- Requires certificate authentication and permits even without a certificate. SSL_VERIFY_FAIL_IF_NO_PEER_CERT-- Requires the client to provide a certificate. But not found alone without certificate will also release SSL_CTX_set_verify (CTX, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); // Set the trusted root certificate if (SSL_CTX_load_verify_locations(CTX, "ca.crt",NULL)<=0){ERR_print_errors_fp(stdout); exit(1); } /* Loads the user's digital certificate, which is sent to the client. The certificate contains public key */ if (SSL_CTX_use_certificate_file(CTX, argv[3], SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stdout); exit(1); } /* Load user private key */ if (SSL_CTX_use_PrivateKey_file(CTX, argv[4], SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stdout); exit(1); } /* Check whether the user private key is correct */ if (! SSL_CTX_check_private_key(ctx)) { ERR_print_errors_fp(stdout); exit(1); } /* Open a socket listener */ if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {perror("socket"); exit(1); } else printf("socket created\n"); bzero(&my_addr, sizeof(my_addr)); my_addr.sin_family = PF_INET; my_addr.sin_port = htons(myport); my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } else printf("binded\n"); if (listen(sockfd, lisnum) == -1) { perror("listen"); exit(1); } else printf("begin listen\n"); while (1) { SSL *ssl; len = sizeof(struct sockaddr); */ if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len)) == -1) {perror("accept"); exit(errno); } else printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd); SSL_new(CTX); SSL_new(CTX); SSL_set_fd(SSL, new_fd); If (SSL_accept(SSL) == -1) {perror("accept"); close(new_fd); break; } ShowCerts(ssl); /* start processing data on each new connection */ bzero(buf, MAXBUF + 1); strcpy(buf, "server->client"); SSL_write(SSL, buf, strlen(buf)); If (len <= 0) {printf(" Message '%s' failed to send! The error code is %d with the error message '%s'\n", buf, errno, strError (errno)); goto finish; } else printf(" message '%s' sent successfully, %d bytes sent! \n", buf, len); bzero(buf, MAXBUF + 1); Len = SSL_read(SSL, buf, MAXBUF); If (len > 0) printf(" received message successfully :'%s', total %d bytes \n", buf, len); Else printf(" Message received failed! Error code %d, error message '%s'\n", errno, strError (errno)); SSL_shutdown(SSL); SSL_shutdown(SSL); SSL_shutdown(SSL); SSL_free(SSL); /* Close the socket */ close(new_fd); } /* Close the socket */ close(sockfd); SSL_CTX_free(CTX); /* SSL_CTX_free(CTX); return 0; }Copy the code
2.3 Client code
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#include <openssl/ssl.h> #include <openssl/err.h> #define MAXBUF 1024 void ShowCerts(SSL * ssl) { X509 *cert; char *line; cert = SSL_get_peer_certificate(ssl); SSL_CTX_set_verify(); // If this function is not enabled, the certificate will be authenticated. If (SSL_get_verify_result(SSL) == X509_V_OK){printf(" Certificate verified \n"); } if (cert ! = NULL) {printf(" Digital certificate info :\n"); line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); Printf (" certificate: %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 certificate info! \n"); } int main(int argc, char **argv) { int sockfd, len; struct sockaddr_in dest; char buffer[MAXBUF + 1]; SSL_CTX *ctx; SSL *ssl; if (argc ! = 5) {printf(" Argument format wrong! The correct usage is as follows :\ n\t\t%s IP address port \n\t For example :\t%s 127.0.0.1 80\n This program is used to receive MAXBUF bytes of messages from a ""IP address of a server port ", argv[0], argv[0]); exit(0); } /* SSL library initialization, see ssl-server.c code */ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { ERR_print_errors_fp(stdout); exit(1); SSL_VERIFY_PEER-- Requires certificate authentication and permits even without a certificate. SSL_VERIFY_FAIL_IF_NO_PEER_CERT-- Requires the client to provide a certificate. But not found alone without certificate will also release SSL_CTX_set_verify (CTX, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); // Set the trusted root certificate if (SSL_CTX_load_verify_locations(CTX, "ca.crt",NULL)<=0){ERR_print_errors_fp(stdout); exit(1); } /* Loads the user's digital certificate, which is sent to the client. The certificate contains public key */ if (SSL_CTX_use_certificate_file(CTX, argv[3], SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stdout); exit(1); } /* Load user private key */ if (SSL_CTX_use_PrivateKey_file(CTX, argv[4], SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stdout); exit(1); } /* Check whether the user private key is correct */ if (! SSL_CTX_check_private_key(ctx)) { ERR_print_errors_fp(stdout); exit(1); } /* Create a socket for TCP communication */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror(" socket "); exit(errno); } printf("socket created\n"); /* Initialize the address and port of the server */ bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(atoi(argv[2])); if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { perror(argv[1]); exit(errno); } printf("address created\n"); If (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest))! = 0) { perror("Connect "); exit(errno); } printf("server connected\n"); SSL_new(CTX); SSL_new(CTX); SSL_set_fd(ssl, sockfd); /* Establish SSL connection */ if (SSL_connect(SSL) == -1) ERR_print_errors_fp(stderr); else { printf("Connected with %s encryption\n", SSL_get_cipher(ssl)); ShowCerts(ssl); } /* Receive MAXBUF bytes */ bzero(buffer, MAXBUF + 1); Len = SSL_read(SSL, buffer, MAXBUF); If (len > 0) printf(" received message successfully :'%s', total %d bytes \n", buffer, len); Else {printf (" Message received failed! Error code %d, error message '%s'\n", errno, strError (errno)); goto finish; } bzero(buffer, MAXBUF + 1); strcpy(buffer, "from client->server"); SSL_write(SSL, buffer, strlen(buffer)); If (len < 0) printf (" Message '%s' failed to send! The error code is %d with the error message '%s'\n", buffer, errno, strError (errno)); Else printf(" Message '%s' sent successfully, %d bytes sent! \n", buffer, len); Finish: /* Close the connection */ SSL_shutdown(SSL); SSL_free(ssl); close(sockfd); SSL_CTX_free(ctx); return 0; }Copy the code
2.4 Certificate Generation
Pay attention to three o ‘clock
First, change the private key encryption password (the -passout parameter) to your own password. The following private keys are generated with the -passout parameter. If the -Nodes parameter is used, you do not need to perform the last step of converting an encrypted RSA key to an unencrypted RSA key.
Second, certificates and keys are given in two forms: direct generation and step generation. The two forms are equivalent and the direct generation form is used here (step generation form is commented).
Third, note that the certificate information is changed to your own organization information. The meanings of the parameters of certificate number are as follows:
C—– Country (Country Name)
ST—- State or Province Name
L—- City (Locality Name)
O—- Company (Organization Name)
OU—- Organizational Unit Name
CN—- Common Name
EmailAddress —-
If you want to read the private key file ca_rsa_private.pem in the future, do not need to enter the password, that is, do not encrypt the private key storage. Replace -passout pass:123456 with -nodes openssl req -newkey rsa:2048 -passout pass:123456 -keyout ca_rsa_private.pem -x509 -days 365 -out ca.crt -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CA/[email protected]" # CA Certificate and Key Generation method 2 ---- Generate CA key and self-signed certificate in steps: # openssl genrsa -aes256 -passout pass:123456 -out ca_rsa_private.pem 2048 # openssl req -new -x509 -days 365 -key ca_rsa_private.pem -passin pass:123456 -out ca.crt -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CA/[email protected] If you want to read the private key file server_rsa_private.pem without entering the password, that is, the private key is not encrypted and stored. Replace -passout pass:server with -nodes openssl req -newkey rsa:2048 -passout pass:server -keyout server_rsa_private.pem -out server.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=SERVER/[email protected]" # Generate the server certificate and key 2 ---- Step by step Generate the server key and certificate to be signed # openssl genrsa -aes256 -passout pass: server-out server_rsa_private.pem 2048 # openssl req -new -key server_rsa_private.pem -passin pass:server -out server.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=SERVER/[email protected]" openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial CRT # Convert the encrypted RSA key to the unencrypted RSA key, avoiding the need to enter the decryption password for each read. For example, enter server. Openssl rsa -in server_rsa_private.pem -out server_rsa_private.pem If you want to read the private key file client_rsa_private.pem in the future, do not need to enter the password, that is, do not encrypt the private key storage. Replace -passout pass:client with -nodes openssl req -newkey rsa:2048 -passout pass: client-keyout client_rsa_private.pem -out client.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CLIENT/[email protected]" # Generating client Certificates and keys 2 ---- Generating client keys and certificates to be signed step by step: # openssl genrsa -aes256 -passout pass:client -out client_rsa_private.pem 2048 # openssl req -new -key client_rsa_private.pem -passin pass:client -out client.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CLIENT/[email protected]" openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial CRT # Convert the encrypted RSA key to the unencrypted RSA key, avoiding the need to enter the decryption password for each read. For example, enter client openssl rsa -in client_rsa_private.pem -out client_rsa_private.pem.unsecureCopy the code
2.5 Development Environment Configuration
Operating system —- Kali-Roaling. In order to use the current virtual machine, using Ubuntu, centos and so on should be the same.
IDE, eclipse. Compiling directly in the terminal does not pass without digging, put Eclipse compile problem directly use Eclipse.
2.5.1 Two projects were created under the same active directory
Myclient1 —- Create a SRC folder for the client code
Myserver1 —- create a SRC folder for the server code
(The other directories are either generated automatically or compiled automatically, never mind; If there are errors in the project, try restarting The Eclipse vOS several times.
2.5.2 Specifying SSL and crypto
Right-click on the project folder —-Properties—- to specify SSL library and crypto library directory otherwise SSL will not be found during compilation. Both projects should be configured
2.5.3 compilation
Eclipse compiles all projects by using the Ctrl+B shortcut
2.5.4 Certificate Replication
Copy the CA certificate (ca.crt), client certificate (client.crt), and unencrypted client private key file () generated earlier to the Debug directory of the myClient1 project
Copy the CA certificate, server certificate, and unencrypted server private key file generated earlier to the Debug directory of the myClient1 project
2.5.5 Running the program
Run the server first and then the client
CRT server_rsa_private.pem.unsecure./ myClient1 127.0.0.1 7838 client.crt client_rsa_private.pem.unsecureCopy the code
The result is as follows, server:
Client: