Two way authentication tutorial of openssl implementation (server code + client code + certificate generation)

Keywords: SSL OpenSSL socket Eclipse

1, Background

1.1 problems

A recent product testing report suggested using pki based authentication. Since the product has implemented https, it is considered that it means using two-way authentication to deal with man in the middle attack.

In information security engineering, two-way authentication has been contacted, but there are two problems.

The first is that the final course design client is browser, and the server is tomcat two-way authentication, which only needs to configure the two and does not need its own real implementation code.

The second is that although the course also has implementation code close to two-way authentication, the Java+JCE environment at that time now uses C+++OpenSSL environment. The overall meaning is almost the same, but the specific functions and parameters are quite different.

So now there are: the idea of certificate generation + the idea of two-way authentication. For readers, it is assumed that they have a basic understanding of several concepts, such as certificate, SSL/TSL, socket programming, etc., which will not be introduced in detail in this paper.

Based on this, the problem to be solved in this paper is: how to generate Certificate in openssl and how to realize bidirectional authentication in openssl.

 

1.2 solutions

1.2.1 openssl specific certificate generation solution

Reference resources https://blog.csdn.net/gengxiaoming7/article/details/78505107 Plus some other articles

 

1.2.2 two way authentication solution for OpenSSL

Use https://blog.csdn.net/sardden/article/details/42705897 The code implements SSL and realizes bidirectional authentication based on it.

The key points of two-way authentication are the following functions (both the server and the client are the same). For others, please refer to the code notes

SSL? CTX? Set? Verify ---- configure enable bidirectional authentication

SSL? CTX? Load? Verify? Locations ---- load trusted root certificate

SSL? CTX? Use? Certificate? File ---- load your own certificate

SSL? CTX? Use? Privatekey? File ---- load your own private key

SSL > get > verify > result ---- to really verify, you must call this function, otherwise the first four optical configurations will not be bidirectional verified

 

2, Two way authentication program implementation

2.1 install openssl and develop api

apt-get install libssl-dev

 

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_get_verify_result()This is the point. SSL_CTX_set_verify()Only when the configuration is enabled or not and the authentication is not executed, can the function be called to verify the authentication
    // If the verification fails, the program throws an exception to terminate the connection
    if(SSL_get_verify_result(ssl) == X509_V_OK){
        printf("Certificate verification passed\n");
    }
    if (cert != NULL) {
        printf("Digital certificate information:\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 information!\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 initialization */
    SSL_library_init();
    /* Load all SSL algorithms */
    OpenSSL_add_all_algorithms();
    /* Load all SSL error messages */
    SSL_load_error_strings();
    /* Generate an SSL? CTX in a SSL V2 and V3 standard compatible way, that is, SSL Content Text */
    ctx = SSL_CTX_new(SSLv23_server_method());
    /* You can also use sslv2? Server? Method() or SSLv3? Server? Method() to represent V2 or V3 standards separately */
    if (ctx == NULL) {
        ERR_print_errors_fp(stdout);
        exit(1);
    }

    // Two way validation
    // SSL_VERIFY_PEER---Certificate certification is required and will be released without certificate
    // SSL_VERIFY_FAIL_IF_NO_PEER_CERT---The client is required to provide a certificate, but it will be released if no certificate is used alone
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
    // Set trust 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 used to send to the client. 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's private key is correct */
    if (!SSL_CTX_check_private_key(ctx)) {
        ERR_print_errors_fp(stdout);
        exit(1);
    }

    /* Turn on a socket monitor */
    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);
        /* Wait for the client to connect */
        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);

        /* A new SSL based on ctx */
        ssl = SSL_new(ctx);
        /* Add the socket of the connected user to SSL */
        SSL_set_fd(ssl, new_fd);
        /* Establish SSL connection */
        if (SSL_accept(ssl) == -1) {
            perror("accept");
            close(new_fd);
            break;
        }
        ShowCerts(ssl);

        /* Start processing data transfer on each new connection */
        bzero(buf, MAXBUF + 1);
        strcpy(buf, "server->client");
        /* Send message to client */
        len = SSL_write(ssl, buf, strlen(buf));

        if (len <= 0) {
            printf("news'%s'Sending failed! The error code is%d,The error message is'%s'\n", buf, errno,
                    strerror(errno));
            goto finish;
        } else
            printf("news'%s'Sent successfully, sent in total%d Byte!\n", buf, len);

        bzero(buf, MAXBUF + 1);
        /* Receive messages from clients */
        len = SSL_read(ssl, buf, MAXBUF);
        if (len > 0)
            printf("Message received successfully:'%s',common%d Bytes of data\n", buf, len);
        else
            printf("Message reception failed! The error code is%d,The error message is'%s'\n",
            errno, strerror(errno));
        /* Processing the end of data receiving and sending on each new connection */
        finish:
        /* Close SSL connection */
        SSL_shutdown(ssl);
        /* Release SSL */
        SSL_free(ssl);
        /* Close socket */
        close(new_fd);
    }
    /* Turn off listening socket */
    close(sockfd);
    /* Release CTX */
    SSL_CTX_free(ctx);
    return 0;
}
View 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_get_verify_result()This is the point. SSL_CTX_set_verify()Only when the configuration is enabled or not and the authentication is not executed, can the function be called to verify the authentication
    // If the verification fails, the program throws an exception to terminate the connection
    if(SSL_get_verify_result(ssl) == X509_V_OK){
        printf("Certificate verification passed\n");
    }
    if (cert != NULL) {
        printf("Digital certificate information:\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 information!\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("Parameter format error! The correct usage is as follows:\n\t\t%s IP Address port\n\t such as:\t%s 127.0.0.1 80\n This program is used to"
             "IP The server with the address receives the most from one port MAXBUF Bytes of messages",
             argv[0], argv[0]);
        exit(0);
    }

    /* SSL For library initialization, refer to 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);
    }

    // Two way validation
    // SSL_VERIFY_PEER---Certificate certification is required and will be released without certificate
    // SSL_VERIFY_FAIL_IF_NO_PEER_CERT---The client is required to provide a certificate, but it will be released if no certificate is used alone
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
    // Set trust 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 used to send to the client. 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 if the user's 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 information of the server (opposite party) */
    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");

    /* Connect to server */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected\n");

    /* A new SSL based on ctx */
    ssl = 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 messages sent by the other party, Max buf bytes at most */
    bzero(buffer, MAXBUF + 1);
    /* Receive message from server */
    len = SSL_read(ssl, buffer, MAXBUF);
    if (len > 0)
        printf("Message received successfully:'%s',common%d Bytes of data\n",
               buffer, len);
    else {
        printf
            ("Message reception failed! The error code is%d,The error message is'%s'\n",
             errno, strerror(errno));
        goto finish;
    }
    bzero(buffer, MAXBUF + 1);
    strcpy(buffer, "from client->server");
    /* Send message to server */
    len = SSL_write(ssl, buffer, strlen(buffer));
    if (len < 0)
        printf
            ("news'%s'Sending failed! The error code is%d,The error message is'%s'\n",
             buffer, errno, strerror(errno));
    else
        printf("news'%s'Sent successfully, sent in total%d Byte!\n",
               buffer, len);

  finish:
    /* Close connection */
    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(sockfd);
    SSL_CTX_free(ctx);
    return 0;
}
View Code

 

2.4 certificate generation

Pay attention to three points.

First, please change the private key encryption password (- passout parameter) to your own password; the private key is generated with the - passout parameter below. If the - nodes parameter is used, the last step "convert the encrypted RSA key to the unencrypted RSA key" does not need to be performed.

Second, the certificate and the key give two forms of direct one-step generation and step-by-step generation. The two forms are equivalent. Here, the direct generation form is used (the step-by-step generation form is annotated)

Third, pay attention to changing the certificate information into your own organization information. The parameters of the certificate number are as follows:

C -- Country Name

ST ---- State or Province Name

L ---- city name

O ---- Organization Name

OU ---- Organizational Unit Name

CN ---- product name (Common Name)

emailAddress ---- Email Address

# CA Certificate and key generation method I----Direct generation CA Key and its self signed certificate
# If you want to read the private key file later ca_rsa_private.pem If you do not need to enter a password, that is, do not encrypt the private key-passout pass:123456 replace 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/emailAddress=youremail@qq.com"
# CA Certificate and key generation method II----Step by step generation CA Key and its self signed certificate:
# 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/emailAddress=youremail@qq.com"

# Server certificate and key generation method I----Generate server key and certificate to be signed directly
# If you want to read the private key file later server_rsa_private.pem If you do not need to enter a password, that is, do not encrypt the private key-passout pass:server replace 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/emailAddress=youremail@qq.com"
# Server certificate and key generation method II----Generate server key and certificate to be signed step by step
# 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/emailAddress=youremail@qq.com"
# Use CA certificate and key to sign server certificate:
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial -out server.crt
# Convert the encrypted RSA key to the unencrypted RSA key, so as to avoid requiring the input of decryption password every time reading
# Password is the passout set when generating the private key file and the passin to be entered when reading the private key file. For example, enter "server" here
openssl rsa -in server_rsa_private.pem -out server_rsa_private.pem.unsecure

# Client certificate and key generation method I----Generate client key and certificate to be signed directly
# If you want to read the private key file later client_rsa_private.pem If you do not need to enter a password, that is, do not encrypt the private key-passout pass:client replace 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/emailAddress=youremail@qq.com"
# Client certificate and key generation method II----Generate client key and certificate 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/emailAddress=youremail@qq.com"
# Use CA certificate and key to sign client certificate:
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial -out client.crt
# Convert the encrypted RSA key to the unencrypted RSA key, so as to avoid requiring the input of decryption password every time reading
# The password is the passout set when generating the private key file and the passin to be entered when reading the private key file. For example, enter "client" here
openssl rsa -in client_rsa_private.pem -out client_rsa_private.pem.unsecure

 

2.5 development environment configuration

Operating system: Kali rolling. In order to use the current virtual machine, there should be no difference in using ubuntu, centos, etc.

IDE----eclipse. Compile directly in the terminal without going deep, put eclipse compile without any problem, and use eclipse directly.

 

2.5.1 build two project s in the same active directory

myclient1 ---- build src folder to put client code

myserver1 ---- build src folder to put server code

(other directories are either automatically generated or automatically generated after compilation, regardless; if there is an error in the project, try restarting the eclipse operating system several times more.)

 

2.5.2 specify ssl and crypto

Right click on the project folder - Properties - specify the ssl library and crypto Library Directory, otherwise the compilation cannot find ssl. Both projects need to be configured

 

2.5.3 compilation

Use Ctrl+B shortcut to compile, eclipse will compile all project s

 

2.5.4 copy of certificate

Copy the CA certificate (ca.crt), client certificate (client.crt), client unencrypted private key file () generated in front to the Debug directory of myclient1 project

Copy the ca certificate, server certificate and unencrypted private key file generated in front to the Debug directory of myclient1 project

 

2.5.5 operation procedure

Run the server first and then the client

./myserver1 7838 1 server.crt server_rsa_private.pem.unsecure
./myclient1 127.0.0.1 7838 client.crt client_rsa_private.pem.unsecure

The operation results are as follows: Server:

Client:

 

Reference resources:

https://blog.csdn.net/gengxiaoming7/article/details/78505107

https://blog.csdn.net/sardden/article/details/42705897

https://blog.csdn.net/Junkie0901/article/details/40402003

https://www.openssl.org/docs/man1.1.1/man3/

https://blog.csdn.net/wangsifu2009/article/details/7569566

https://www.cnblogs.com/zhouxihi/p/6001866.html

Posted by yankeefan238 on Mon, 13 Apr 2020 05:09:17 -0700