openssl genrsa -aes256 -out ./<private-key-name>.key 4096
Posted by nerdcoding on January 20, 2019
In my last blog post, I laid the foundation in How TLS/SSL and X.509 really works. Here I will provide the practical part. How to generate private and public keys. How the create self-signed certificates and certificate chains. How do you get a certificate signed by a real certificate authority (CA). And how to integrate all this into an nginx web server and a self-developed Spring Boot server.
The first step is to generate an RSA private key. The private key should not be saved in plaintext but encrypted with
AES256
. Therefore a password is always needed for decryption when the private key is accessed. Anymore as the length
of the private we use 4096 bit:
openssl genrsa -aes256 -out ./<private-key-name>.key 4096
Verify the private key:
openssl rsa -in <private-key-name>.key -check
Before a certificate could be signed a Certificate Signing Request (CSR) is needed. Such a CSR contains the public key, the counterpart to the private key, and some additional information about the person or organization that would like to have it’s CSR signed. As hash algorithm we choose SHA256. Google announced a few years ago, that certificate chains using SHA-1 will no longer appear to be fully trustworthy in Chrome.
openssl req -new -sha256 -key ./<private-key-name>.key -out ./<csr-name>.csr
When this command is executed, the password of the private key is needed and the additional information needs to be provided. These are:
Country Name (2 letter code) - C
State or Province Name (full name) - ST
Locality Name (eg, city) - L
Organization Name (eg, company) - O
Organizational Unit Name (eg, section) - OU
Common Name (e.g. server FQDN or YOUR name) - CN
The common name is important and needs to be the fully qualified domain (FQDN) of the server the certificate is integrated. Only when the servers DNS name and the CN of the certificate matches, the browser will consider the certificate as safe (→ Common Name Mismatch problem).
Verify the Certificate Signing Request:
openssl req -noout -text -in ./<csr-name>.csr
To self-sign the certificate, take the CSR and sign it with the previously generated private key. Each certificate must
have a validity period which could be parameterized with the -days
flag:
openssl x509 -signkey ./<private-key-name>.key -in ./<csr-name>.csr -req -days 365 -out <certificate-name>.crt
Verify the created X.509 certificate:
openssl x509 -text -noout -in <certificate-name>.crt
In the last step, the certificate signed itself with its own private key. When a whole certificate chain is needed we also create a self-signed root certificate. But then all the following certificates are signed with the previous one. We create a self-signed root certificate:
-- Create RSA private key openssl genrsa -aes256 -out root.key 4096 -- Create a Certificate Signing Request (CSR) openssl req -new -sha256 -key root.key -out root.csr -subj "/C=DE/ST=Hamburg/L=Hamburg/O=My Company Ltd/CN=root.example.com" -- Create self-signed certificate openssl x509 -signkey root.key -in root.csr -req -days 730 -out root.crt
The next certificate in the chain is the grandparent certificate, which is now signed by the previously created root certificate:
-- Create RSA private key openssl genrsa -aes256 -out grandparent.key 4096 -- Create a Certificate Signing Request (CSR) openssl req -new -sha256 -key grandparent.key -out grandparent.csr -subj "/C=DE/ST=Hamburg/L=Hamburg/O=My Company Ltd/CN=grandparent.example.com" -- Create certificate signed by the root certificate openssl x509 -CA root.crt -CAkey root.key -CAcreateserial -in grandparent.csr -req -days 365 -out grandparent.crt
Then we repeat this step to times and create a parent certificate, signed by the grandparent certificate:
-- Create RSA private key openssl genrsa -aes256 -out parent.key 4096 -- Create a Certificate Signing Request (CSR) openssl req -new -sha256 -key parent.key -out parent.csr -subj "/C=DE/ST=Hamburg/L=Hamburg/O=My Company Ltd/CN=parent.example.com" -- Create certificate signed by the grandparent certificate openssl x509 -CA grandparent.crt -CAkey grandparent.key -CAcreateserial -in parent.csr -req -days 365 -out parent.crt
And a server certificate, signed by the parent certificate:
-- Create RSA private key openssl genrsa -aes256 -out server.key 4096 -- Create a Certificate Signing Request (CSR) openssl req -new -sha256 -key server.key -out server.csr -subj "/C=DE/ST=Hamburg/L=Hamburg/O=My Company Ltd/CN=server.example.com" -- Create a certificate signed by the parent certificate openssl x509 -CA parent.crt -CAkey parent.key -CAcreateserial -in server.csr -req -days 365 -out server.crt
Now we can combine all these certificates to a certificate chain. Simply use cat
to concatenate each file:
cat server.crt parent.crt grandparent.crt root.crt > chain.pem
Let’s encrypt is a free certificate authority (CA) which provides many ways to obtain a signed certificate which is considered as safe by the most browser. To obtain a certificate by let’s encrypt you need a real DNS name accessible by the let’s encrypt servers.
Our recently created self-signed certificate chain (chain.pem
) and the associated private key (server.key
) could be integrated into an nginx web server installation and used to secure a connection to nginx with TLS/SSL. Anymore the
private key is needed by nginx to decrypt incoming messages. During the private key creation, we encrypted the private
key with AES256 so that a password is needed to read the key. During every server startup of nginx the server prompts
for this password. To prevent this firstly we remove the encryption from the private key:
openssl rsa -in server.key -out server.keynopass
Then in the nginx.conf
we simply configure two servers. One listening on the default port 80 and redirects requests
to port 443 (https). And the second one listens on port 443 with the SSL configuration. Here the private key
server.keynopass
and the self-signed certificate chain chain.pem
needs to be provided.
worker_processes 1; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; # Forward requests to port 443 (https) server { listen 80 default_server; listen [::]:80 default_server; server_name _; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name localhost; ssl_certificate ./chain.pem ssl_certificate_key ./server.keynopass; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; location / { root /usr/share/nginx/html; index index.html index.htm; } } }
When we now start our nginx server and let our browser send a request, the browser will show a warning that the connection is not private:
Each browser keeps a list of trustworthy CAs and when a server provides a certificate, the browser checks if this
certificate was signed by one of these CAs and if so, asks the CA if the certificate is valid. In our case the
certificate was self-signed and the browser marks it as ERR_CERT_AUTHORITY_INVALID
. With Advanced → Proceed to…
we can ignore this error message and force the browser to accept this TLS/SSL connection anyway.
The browsers certificate viewer will now show the previously created certificate chain:
When using TLS/SSL for Java applications we normally wouldn’t directly use the private key and certificate chain. Both
are encapsulated into a Java KeyStore. To create a KeyStore JDKs default keytool
could be used. The most simple way
is to combine both, the certificate chain and the private key in an PKCS#12
archive file format and then use this .p12 file to create a Java KeyStore. First create PKCS#12 file:
openssl pkcs12 -export -in chain.pem -inkey server.key -certfile chain.pem -name server -out server.p12
And then the Java KeyStore:
keytool -importkeystore -srckeystore server.p12 -srcstoretype pkcs12 -destkeystore java-key-store.jks -deststoretype JKS
To check the KeyStore use:
keytool -list -v -keystore <keystore-name>.jks
Finally, we could use the created Java KeyStore to integrate into a Spring Boots application.properties
(or
application.yml
) to secure connections to this application with TLS/SSL.
server.port=8443
server.ssl.enabled=true
server.ssl.key-store-type=JKS
server.ssl.key-store=/path/to/java-key-store.jks # (or server.ssl.key-store=classpath:java-key-store.jks)
server.ssl.key-store-password=secret
server.ssl.key-alias=server