If you want to use encrypted connections for a host that you control, you need a TLS certificate. You can create a self-signed TLS certificate, but unless it has been signed by a Certifying Authority (like Verisign), you'll see a browser warning that the site is not secure. Here, you'll learn how to become your own certifying authority for hosts used internally.
A better approach is to become your own local Certifying Authority. Here, you'll learn how to become your own certifying authority for hosts used internally.
Your browser will not trust a certificate that has not been signed by a Certifying Authority. You'll need a root certificate with a private key in order to employ a certifying authority to sign your certificates for use by your host's server processes, like a web server or app server. In this order, do the following:
- Generate a private key. You'll be prompted for a passphrase.
openssl genrsa -des3 -out selfCA.key 4096
- Generate a root certificate, which needs to be installed on each device from which you will access the host. You'll be prompted for the same passphrase from the previous step:
openssl req -x509 -new -nodes -key ./selfCA.key -sha256 -days 1820 -out ./selfCA.pem
- Install the root certificate for your signing authority. Here are instructions for Ubuntu 18.04 LTS:
sudo mkdir /usr/share/ca-certificates/extra
# Converts from pem to crt
sudo openssl x509 -in ./selfCA.pem -inform PEM -out /usr/share/ca-certificates/extra/selfCA.crt
sudo dpkg-reconfigure ca-certificates
These steps should theoretically let you browse to your host's urls on port 8443 without the "Your connection is not private" message. However, neither Chrome or Firefox use system-wide certificate authorities. You need to configure each browser manually. Instructions for both Chrome and Firefox found here:
- On Chrome, go to
chrome://settings/certificates
, click on Authorities, choose Import and choose your selfCA.crt - On Firefox, go to
about:preferences#advanced
, click Security, click View Certificates, under Certificates, click Authorities, then import your selfCA.crt file
Note that in your browser's location bar, you must use URL https://myhost:443
. localhost will not work.
Now, we can create CA-signed certificates for the host
- Create a private key
openssl genrsa -out myhost.key 4096
- Create a certificate signing request (CSR)
openssl req -new -key myhost.key -out myhost.csr
- Create a config file to define the Subject Alternative Name (SAN) extension called myhost.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = myhost
- Create the certificate
openssl x509 -req -in myhost.csr -CA selfCA.pem -CAkey selfCA.key -CAcreateserial -out myhost.crt -days 1820 -sha256 -extfile myhost.ext
Now there are three files for your server host: myhost.key (the private key), myhost.csr (the certificate signing request), and myhost.crt (the signed certificate).
selfCA.key
: private key for my personal Certificate Authority (CA)
selfCA.pem
: root certificate, which needs to be installed on each device from which you will access the host
selfCA.crt
: converted from PEM file above to crt. Some indications you can just rename .pem to .crt, not sure
myhost.key
: private key for my host
myhost.csr
: the certificate signing request for the host
myhost.ext
: config file in Subject Alternative Name (SAN) extension format for the host
myhost.crt
: the host certificate
myhost.keystore
: combines the private key and the certificate for myhost into a PKCS12 keystore
localhost-rsa.jks
: the Java keystore used by Tomcat (see below)
I copied all files to /opt/certs, changed account ownership to root,group ownership to ssl-cert, and mode to 710 (rwx--x---). I also changed user and group to root:ssl-cert on all files in /opt/certs. Lastly, I set the mode on the .key files to 640.
Be sure that any services that require access to the crt and key files are in the ssl-cert group (like the owner of the NodeJS process).
Now you need to configure your server processes with the private key and certificate.
Confirmed with nginx version 1.17.3 on Ubuntu 18.04.3 Change /etc/nginx/conf.d as follows
ssl_certificate path_to_server_certificate;
ssl_certificate_key path_to_server_private_key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
- Combine the private key and the certificate into a PKCS12 keystore
openssl pkcs12 -export -in myhost.crt -inkey myhost.key -out myhost.keystore -name tomcat -CAfile selfCA.crt -caname root
(used password "changeit", which is the default password for keytool(?)) 2. Merge the Tomcat keystore and the PKCS12 keystore to import the certificate and private key
keytool -importkeystore -deststorepass changeit -destkeypass changeit -destkeystore localhost-rsa.jks -srckeystore myhost.keystore -srcstoretype PKCS12 -srcstorepass changeit -alias tomcat
- Copy the keystore file into Tomcat's conf/ directory. IMPORTANT: if you need to install a second keystore in Tomcat, you can't copy it, you need to merge it using 2 above
sudo cp ./localhost-rsa.jks /var/lib/tomcat8/conf
- Add (or likely, uncomment) the following from Tomcat's server.xml file:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
type="RSA" />
</SSLHostConfig>
</Connector>
- Restart tomcat
sudo service tomcat8 restart
- Add the following to your server's js file
var fs = require('fs');
var https = require('https');
- Copy your host's private key and crt files in an accessible directory. See Permissions to protect your private key.
- Copy your certifying authority's root certificate in an accessible location. See Permissions to protect your private key.
- Add the following to your server's js file
var key = fs.readFileSync('/opt/certs/myhost.key');
var cert = fs.readFileSync( '/opt/certs/myhost.crt' );
var ca = fs.readFileSync( '/opt/certs/selfCA.crt' );
var options = {
key: key,
cert: cert,
ca: ca
};
var port = 8081;
https.createServer(options, app).listen(port);
// app.listen(port); // Unencrypted connections
console.log('Application listening on port ' + port);
- Add
gem 'thin'
to your Gemfile - Add the following to your application's server file:
class App < ::Thin::Backends::TcpServer
def initialize(host, port, options)
super(host, port)
@ssl = true
@ssl_options = options
end
end
configure do
set :environment, :production
set :bind, '0.0.0.0'
set :port, 4567
set :server, "thin"
class << settings
def server_settings
{
:backend => App,
:private_key_file => "/path_to_server_private_key",
:cert_chain_file => "path_to_server_certificate",
:verify_peer => false
}
end
end
Using openssl
, you can verify the passphrase of your private key. Unfortunately, openssl
requires the passphrase to be provided on the command line. To prevent your passphrase from being stored in clear text, use the read command as follows:
$ read -s -p "Password:" PASSWORD
Password: <enter the passphrase for the private key>
$ openssl rsa -noout -in myserverCA.key -passin "pass:$PASSWORD"
Notice that no response means that your passphrase is valid. If the passphrase is invalid, openssl
will respond as follows:
$ openssl rsa -noout -in myserverCA.key -passin "pass:$PASSWORD"
unable to load Private Key
140307636995392:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:../crypto/evp/evp_enc.c:610:
140307636995392:error:0906A065:PEM routines:PEM_do_header:bad decrypt:../crypto/pem/pem_lib.c:461: