Some time ago it became necessary within the project to limit the simultaneous number of computers that have access to a web application running within the customer’s local network.
The decision to use USB hardware tokens to identify the computer came naturally. The choice was Rutoken EDS: it works without drivers, only a browser plugin produced by the developer is needed to work in the Web application. Since the token should identify the computer, not the user, the work with it should be completely “transparent”: if it exists, the system simply silently works, without asking the user any extra questions.
The decision was made: when logging into the system, sign the user's identification data with an unqualified signature of the certificate stored on the Rutokene using the Rutoken-plugin means, and verify the server on the server. After successful login, using the plug-in means to check the physical presence of that token itself, and in its absence, exit the system. In the framework of this project was enough. ')
There is a need to create your own Certification Authority (CA) for the exchange of signed messages, or rather, to transfer signed messages from the client to the server. Client certificates must be located on USB tokens in private key containers, and signature verification must be performed on the server using OpenSSL
So, the task: installation and configuration on the Linux server of the TC.Deploy client certificates that identify computers on USB tokens .
To solve it you will need:
OpenSSL 1.1.0 and higher, with support for GOST algorithms
Create a directory where the CA will be located and copy the OpenSSL configuration file that comes with the system (on the latest versions of Ubuntu, this is /etc/ssl/openssl.cnf )
Configuring "our" openssl.cnf:
a) Add to the beginning of the file directives for connecting the token engine:
d) in the [v3_ca] section, remove the critical option from the basicConstraints parameter:
basicConstraints = CA:true
For what?Honest answer: I do not know.However, all the examples of root certificates that I downloaded in the process of trying to figure out the topic were without a critical sign.I address the question “for what?” To more experienced colleagues in the question.
e) optionally set default values ​​that will be offered when issuing self-signed certificates and generating requests for issuing client certificates. These parameters are in the [req_distinguished_name] section
The parameter with the _default postfix is ​​the default value. Example:
countryName = Country Name (2 letter code) countryName_default = AU countryName_min = 2 countryName_max = 2
When the system asks to enter the countryName parameter, it will indicate in square brackets that it will leave the AU value by default.
This completes the configuration of the OpenSSL config. It remains to indicate OpenSSL that it is necessary to use it. To do this, set the environment variable OPENSSL_CONF:
export OPENSSL_CONF=/path/to/your/openssl.cnf
We create a directory structure where information about our CA will be stored. To do this, go to the created directory with the newly edited openssl.cnf, and perform the following actions:
a) create subdirectories in it:
demoCA demoCA / private demoCA / newcerts
Note: the demoCA name is spelled out in the [CA_default] section of the openssl.cnf file.You can change it (in step 2) and then instead of working with demoCA.
b) in the demoCA directory we create an empty index.txt file and a serial file, which we open with a text editor and write the line 01 there. This is the counter of issued certificates. After the release of each successive certificate, the value in this file is incremented by one.
Optionally, we format our token using the rtAdmin utility. Now everything is ready for CA deployment.
The action algorithm is in general simple:
a) we issue the root certificate of the certificate authority using the GOST algorithm:
generating the private key for issuing a self-signed CA certificate
generate the X509 self-signed certificate using the generated key
b) on each of the USB tokens
generate a key pair (the so-called private key container)
generate a certificate signing request using the generated token key
issue a certificate for this request
save the certificate on the token in the container of the private key
The following is the implementation of this algorithm for a single token:
Generating the private key for the CA certificate (using the GOST algorithm):
Please note: we specified on the command line that it is necessary to use the v3_ca extensions from the openssl_cnf config. It is written there that this is our CA. Duration 10 years. The usual thing for CA. But you can and more.
In the process of issuing a certificate, the system will ask you to enter the values ​​of the parameters that are in the [req_distinguished_name] section of our openssl.cnf file
Now we start operations with a token. If the token is new or formatted with default values, then the user's PIN on it is 12345678. I assume that this is exactly the case. Otherwise, you must specify the correct user PIN and generally try so that in the examples below, the names of the objects already existing on the token do not overlap with the entered ones.
First, generate a key pair. OpenSSL does not know how to perform this operation on Rutoken, so we will use the pkcs11-tool utility from the OpenSC package:
Important note: we specified id 303030303031. Every two digits of this id are nothing more than the ASCII code of the characters "0" and "1", respectively.When operating with OpenSSL, it will look like “id = 000001”
will request the parameters of the certificate name (from the [req_distinguished_name] section)
will issue a certificate signing request file
Using this request, we sign the client certificate ( in the example, the certificate validity period is 1825 days. It is important that this period does not exceed the validity period of your root certificate ):
The system will display the certificate, ask about the decision to sign it (answer “y”), and about the decision to save the new certificate (again answer “y”).
If everything is done correctly, the system will request a PIN, sign the message, then verify the signature and, if successful, display the original message and the result of the check (“success”)
Remark Returning to the title task and the signature by means of the plug-in, it should be noted that by default the plug-in gives the result of the signature not in the PEM format, but in the DER format, encoded in base64. Therefore, to verify the signature, you must first decode from base64, and specify the input format DER when checking.