How to trust self-signed localhost certificates on Linux Chrome and Firefox - google-chrome

I try to generate a self-signed certificate for a custom local domain pointing to 127.0.0.1:
# /etc/hosts
127.0.0.1 subdomain.domain.local
I've generated a self-signed certificate using openssl and remember that everything worked in the past. But it seems that since Chrome 58, there are far more restrictions on using self-signed certificates.
My attempts conclude with "Your connection is not private" following with one of the below errors:
"security certificate is not trusted" if I proceed like in the past.
"not a certification authority" when trying to import it into Chrome.
"subject alternative name missing" when using a certificate after importing its CA.
I'm pretty sure I'm missing something in the process. Please, can anyone provide the valid configuration to handle alternative names along with the exact steps to create the corresponding CA and a certificate so that Chrome and Firefox can handle my local custom domain?

TLDR
Create the file generate.sh
#!/usr/bin/env bash
find . \( -name "$1.*" -o -name "*.srl" \) -type f -delete
cp /usr/lib/ssl/openssl.cnf $1.cnf
python <(
cat << "END"
import sys
from ConfigParser import ConfigParser
from StringIO import StringIO
domain = sys.argv[1]
config = ConfigParser()
config.optionxform = lambda option: option
name = "{}.cnf".format(domain)
with open(name, "rb") as stream:
config.readfp(StringIO("[top]\n" + stream.read()))
config.set(" v3_ca ", "subjectKeyIdentifier", "hash")
config.set(" v3_ca ", "authorityKeyIdentifier", "keyid:always,issuer")
config.set(" v3_ca ", "basicConstraints", "critical, CA:TRUE, pathlen:3")
config.set(" v3_ca ", "keyUsage", "critical, cRLSign, keyCertSign")
config.set(" v3_ca ", "nsCertType", "sslCA, emailCA")
config.set(" v3_req ", "basicConstraints", "CA:FALSE")
config.set(" v3_req ", "keyUsage", "nonRepudiation, digitalSignature, keyEncipherment")
config.set(" v3_req ", "subjectAltName", "#alt_names")
config.remove_option(" v3_req ", "extendedKeyUsage")
config.add_section(" alt_names ")
config.set(" alt_names ", "DNS.1", domain)
config.set(" alt_names ", "DNS.2", "*.{}".format(domain))
config.set(" req ", "req_extensions", "v3_req")
with open(name, "wb") as stream:
config.write(stream)
END
) $1
tail -n +2 $1.cnf > $1.cnf.tmp && mv $1.cnf.tmp $1.cnf
echo "$1\n" | openssl genrsa -aes256 -out $1.ca.key 2048
chmod 400 $1.ca.key
openssl req -new -x509 -subj "/CN=$1" -extensions v3_ca -days 3650 -key $1.ca.key -sha256 -out $1.ca.crt -config $1.cnf
openssl genrsa -out $1.key 2048
openssl req -subj "/CN=$1" -extensions v3_req -sha256 -new -key $1.key -out $1.csr
openssl x509 -req -extensions v3_req -days 3650 -sha256 -in $1.csr -CA $1.ca.crt -CAkey $1.ca.key -CAcreateserial -out $1.crt -extfile $1.cnf
openssl x509 -in $1.crt -text -noout
Call ./generate.sh example.com
Requires Python 2
All credits go to this excellent article by Fabian Lee.
Create a trusted CA and SAN certificate using OpenSSL
Customize openssl.cnf
Create CA certificate
Create Server certificate with SAN signed by CA
Prerequisite
As a prerequisite, ensure the SSL packages are installed:
$ sudo apt install libssl1.0.0 -y
Customized openssl.cnf
The first step is to grab the openssl.cnf template available on your system. On Ubuntu this can be found at /usr/lib/ssl/openssl.cnf. You may find this in /System/Library/OpenSSL/ on MacOS, and /etc/pki/tls on Redhat variants.
export prefix="mydomain"
cp /usr/lib/ssl/openssl.cnf $prefix.cnf
$prefix.cnf needs be modified with the specific information about the cert we are going to generate.
Under the [ v3_ca ] section, add the following values. For the CA, this signifies we are creating a CA that will be used for key signing.
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical, CA:TRUE, pathlen:3
keyUsage = critical, cRLSign, keyCertSign
nsCertType = sslCA, emailCA
Then under the [ v3_req ] section, set the following along with all the valid alternative names for this certificate.
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
#extendedKeyUsage=serverAuth
subjectAltName = #alt_names
[ alt_names ]
DNS.1 = mydomain.com
DNS.2 = *.dydomain.com
Also uncomment the following line under the [ req ] section so that certificate requests are created with v3 extensions.
req_extensions = v3_req
When we generate each type of key, we specify which extension section we want to use, which is why we can share $prefix.cnf for creating both the CA as well as the SAN certificate.
Create CA Certificate
Now we will start using OpenSSL to create the necessary keys and certificates. First generate the private/public RSA key pair:
openssl genrsa -aes256 -out ca.key.pem 2048
chmod 400 ca.key.pem
This encodes the key file using an passphrase based on AES256.
Then we need to create the self-signed root CA certificate.
openssl req -new -x509 -subj "/CN=myca" -extensions v3_ca -days 3650 -key ca.key.pem -sha256 -out ca.pem -config $prefix.cnf
You can verify this root CA certificate using:
openssl x509 -in ca.pem -text -noout
This will show the root CA certificate, and the Issuer and Subject will be the same since this is self-signed. This is flagged as CA:TRUE meaning it will be recognized as a root CA certificate; meaning browsers and OS will allow it to be imported into their trusted root certificate store.
Issuer: CN=myca
...
Subject: CN=myca
...
X509v3 Basic Constraints:
critical CA:TRUE, pathlen:3
X509v3 Key Usage:
critical Certificate Sign, CRL Sign
Netscape Cert Type:
SSL CA, S/MIME CA
Create Server certificate signed by CA
With the root CA now created, we switch over to the server certificate. First generate the private/public RSA key pair:
openssl genrsa -out $prefix.key.pem 2048
We didn’t put a passphrase on this key simply because the CA is more valuable target and we can always regenerate the server cert, but feel free to take this extra precaution.
Then create the server cert signing request:
openssl req -subj "/CN=$prefix" -extensions v3_req -sha256 -new -key $prefix.key.pem -out $prefix.csr
Then generate the server certificate using the: server signing request, the CA signing key, and CA cert.
openssl x509 -req -extensions v3_req -days 3650 -sha256 -in $prefix.csr -CA ca.pem -CAkey ca.key.pem -CAcreateserial -out $prefix.crt -extfile $prefix.cnf
The $prefix.key.pem is the server private key and $prefix.crt is the server certificate. Verify the certificate:
openssl x509 -in $prefix.crt -text -noout
This will show the certificate, and the Issuer will be the CA name, while the Subject is the prefix. This is not set to be a CA, and the Subject Alternative Name field contains the URLs that will be considered valid by browsers.
Issuer:
CN=myca
...
Subject:
CN=mydomain
...
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Subject Alternative Name:
DNS:mydomain.com, DNS:*.mydomain.com
Browser Evaluation
When you first point Chrome or Firefox at the site with your SAN cert with CA signing, it will throw the same type of exceptions as a self-signed SAN cert. This is because the root CA cert is not known as a trusted source for signed certificates.
Chrome
Linux
On Linux, Chrome manages its own certificate store and again you should import ca.pem into the Authorities. This should now make the security icon turn green.
Windows
In Chrome settings (chrome://settings), search for certificates and click on Manage Certificates. On Windows this will open the Windows certificate manager and you should import the ca.pem file at the Trusted Root Certification Authorities tab. This is equivalent to adding it through mmc.exe, in the local user trusted root store (not the computer level).
Firefox
In Firefox Options about:preferences, search for certificates and click View Certificates. Go to the Authorities tab and import ca.pem. Check the box to have it trust websites, and now the lock icon should turn green when you visit the page.

Related

What is the correct way to generate a selfsigned cert for *.localhost ('wildcard')

I can't for the life of me get chrome to work with a wildcard cert ("*.localhost")
Here is what I'm doing to generate said cert.
First I gen my ca
openssl genrsa -out priv/cert/ca.key 2048
openssl req -new -x509 -nodes -subj "/C=US/O=_Development \
CA/CN=Development certificates" -key priv/cert/ca.key -sha256 \
-days 3650 -out priv/cert/ca.crt
Then I gen my localhost
openssl genrsa -out priv/cert/localhost.key 2048
openssl req -new -subj "/C=US/O=Local Development/CN=*.localhost" -key \
priv/cert/localhost.key -out priv/cert/localhost.csr
Then I make my ext file
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = #alt_names
[req]
req_extensions = req_ext
[req_distinguished_name]
commonName_default = localhost
[req_ext]
subjectAltName = #alt_names
[alt_names]
DNS.1 = *.localhost
DNS.2 = foo.localhost
DNS.3 = localhost
Then I sign.
openssl x509 -req \
-in priv/cert/localhost.csr \
-extfile priv/cert/localhost.ext \
-CA priv/cert/ca.crt \
-CAkey priv/cert/ca.key \
-CAcreateserial \
-out priv/cert/localhost.crt \
-days 365 \
-sha256
After trusting my crt and navigate locally to say localhost or foo.localhost I get all green but if I go to say bar.localhost the wild card does not work and I get a NET::ERR_CERT_COMMON_NAME_INVALID on chrome.
what am I missing here. I've addressed the Subject Alternative Name, I've ruled out that SAN via foo works. Common name does not even matter anymore from what I read. I'm at a loss.
TL;DR: Browsers will not accept *.localhost in a certificate.
In *.localhost the localhost suffix is treated as a top level domain (TLD) and wildcards directly below a TLD are not allowed. The idea behind this is basically that no single organization actually owns a TLD like com and thus allowing a certificate for *.com would be pretty dangerous. Therefore *.localhost will not be accepted, while *.foo.localhost will.
For more on this see Wildcard *.localhost SSL with Nginx and Chrome and Can a wildcard SSL certificate be issued for a second level domain?.

Unable to connect to MariaDB via SSL on Google Apps Script

I'm having difficulties connecting Google Apps Script to my MariaDB instance.
This is the GAS I am using for this example:
https://gist.github.com/HappymanOkajima/8740727662e9ba0e0ffd52006484c47f
The MariaDB instance is from the original dockerhub image: mariadb:10.4
I have successfully configured MariaDB to run with SSL (verified with Metabase, and shows 'havessl'='YES')
For context, the following commands were used to generate ssl keys:
Generate key for CA-cert
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 > ./ca-key.pem
Generate CA-cert
openssl req -new -x509 -nodes -days 36500 -key ./ca-key.pem -out ./ca-cert.pem -subj "/C=US/ST=NY/L=NTC/O=company/CN=company"
Create SQL server key
openssl req -newkey rsa:4096 -nodes -keyout ./server-key.pem -out ./server-req.pem -subj "/C=US/ST=NY/L=NTC/O=company/CN=sql.server.com"
Create SQL server cert
openssl x509 -req -in ./kn-req.pem -days 36500 -CA ./ca-cert.pem -CAkey ./ca-key.pem -CAcreateserial -out ./server-cert.pem
Convert server key to RSA (mariadb only reads this)
openssl rsa -in ./server-key.pem -out ./server-key-rsa.pem
SQL server is configured with ca-cert.pem, server-cert.pem and server-key-rsa.pem
Create Client Key (for Supermetrics)
openssl req -newkey rsa:4096 -nodes -keyout ./client-key.pem -out ./client-req.pem -subj "/C=US/ST=NY/L=NTC/O=company/CN=supermetrics"
Create client cert
openssl x509 -req -in ./client-req.pem -days 36500 -CA ./ca-cert.pem -CAkey ./ca-key.pem -CAcreateserial -out ./client-cert.pem
Create RSA version, for Supermetrics
openssl rsa -in ./client-key.pem -out ./client-key-rsa.pem
I then used cat to print these files: ca-cert.pem, client-cert.pem, client-key.pem
And copy-pasted the strings into the Google Apps Script, appending \n\ to the end of each line.
This is the error that shows:
Exception: Failed to establish a database connection. Check connection string, username and password.
I have also verified that if I removed ?useSSL=true, the connection is completed successfully
What am I missing here? I have scoured every google dev forum posting and stackoverflow questions on this, and they all seem to say that this is the correct solution. Is anyone able to replicate this to verify?

client certificate authentication not working with chrome and apache2 server

I am attempting to use client certificates to limit secure access to an apache2 web server. However after installation google chrome returns a ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED error.
First I set up the CA for the Web Server by creating a CA key and an X509 PEM file:
openssl genrsa -out CA.key 2048
openssl req -x509 -new -nodes -key CA.key -days 7300 -out CA.pem
I already have an existing certificate for the web site set in apache2 for https communication allocated by a trusted third party. The following is the apache2 conf setting for this website where I have included SSLCACertificateFile for the certificate generated above, the SSLOptions, SSLVerifyClient and SSLVerfiyDepth directives:
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName site.aname.com
ServerAdmin webmaster#localhost
DocumentRoot /var/www/html/site
<Directory /var/www/html/site>
Options FollowSymLinks
AllowOverride All
Require all granted
SSLOptions +StdEnvVars
SSLVerifyClient require
SSLVerifyDepth 1
</Directory>
LogLevel debug
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLCACertificateFile /etc/apache2/ssl/site/CA.pem
SSLCertificateFile /etc/apache2/ssl/site/fullchain.pem
SSLCertificateKeyFile /etc/apache2/ssl/site/privkey.pem
Include /etc/apache2/ssl/site/options-ssl-apache.conf
</VirtualHost>
</IfModule>
This completes the web server configuration and test without a client certificate and get the expected error.
I then generate a client certificate and sign with the CA and then package in pkcs12 with private key with the following:
GENERATE:
openssl genrsa -out user.key 2028
openssl req -new -key user.key -out user.csr
SIGN WITH:
openssl x509 -sha256 -req -in user.csr -out user.crt -CA CA.pem -CAkey CA.key -CAcreateserial -days 1095
CREATE PKCS12:
openssl pkcs12 -export -out user.pfx -inkey user.key -in user.crt
The resultant user.pfx is then installed on the user machines running chrome.
When attempting to connect, chrome asks for the key which is selected with the result being the error ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED.
Version of Chrome running under Windows 10 is: Version 74.0.3729.169 (Official Build) (64-bit)
Update:
I can connect successfully with curl from another linux server using:
curl --cert user.crt --key user.key --pass password https://site.aname.com/
however, the same from windows 10 command line results in:
curl: (35) schannel: next InitializeSecurityContext failed: Unknown error (0x80090027) - The parameter is incorrect
Solved by ensuring that the signed user certificate was a "version 3" X.509 certificate and specifying both Key Usage and Enhanced Key Usage attributes within the v3 extension. This was achieved my modifying the openssl.conf file for the X509 sign request or if you use the openssl CA command.

Setting up MySQL SSL connections

My task is to make all local and remote connections to MySQL encrypted and all clients must be verified by client SSL certificates.
But I can't even connect to MySQL from shell and always getting 'Access denied for user ssluser#localhost...)'
Platform:
Amazon EC2 micro with Amazon Linux AMI (all updates installed)
MySQL 5.7.7
I have created the self-signed certificate in accordance with this instruction
# Create CA certificate
# -----------
# CN = localdomain.com
$ openssl genrsa 2048 > ca-key.pem
$ openssl req -new -x509 -nodes -days 3600 -key ca-key.pem -out ca.pem
# Create server certificate, remove passphrase, and sign it
# server-cert.pem = public key, server-key.pem = private key
# -----------
# CN = cn1.localdomain.com
$ openssl req -newkey rsa:2048 -days 3600 -nodes -keyout server-key.pem -out server-req.pem
$ openssl rsa -in server-key.pem -out server-key.pem
$ openssl x509 -req -in server-req.pem -days 3600 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
# Create client certificate, remove passphrase, and sign it
# client-cert.pem = public key, client-key.pem = private key
# -----------
# CN = cn2.localdomain.com
$ openssl req -newkey rsa:2048 -days 3600 -nodes -keyout client-key.pem -out client-req.pem
$ openssl rsa -in client-key.pem -out client-key.pem
$ openssl x509 -req -in client-req.pem -days 3600 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem
I have created the MySQL DB user for testing
CREATE USER 'ssl-user'#'%' identified by '123';
GRANT USAGE ON *.* TO 'ssluser'#'%' identified by '123' REQUIRE X509;
FLUSH PRIVILEGES;
Edited my.cnf
[mysqld]
ssl-ca=/etc/pki/mysql_ssl/ca.pem
ssl-cert=/etc/pki/mysql_ssl/server-cert.pem
ssl-key=/etc/pki/mysql_ssl/server-key.pem
[client]
ssl-cert=/etc/pki/mysql_ssl/client-cert.pem
ssl-key=/etc/pki/mysql_ssl/client-key.pem
and restarted mysqld...
Than I am trying to connect from shell
mysql -ussluser -p123123123 --ssl-cert=/etc/pki/mysql_ssl/client-cert.pem --ssl-key=/etc/pki/mysql_ssl/client-key.pem
And always get 'Access denied for ssluser#localhost (using password: YES).
I also tried to use our purchased WildCard Comodo Certificates to only encrypt the connection (but not verify the client) with no success.
I am a bit confused because I know a lot of people actually do MySQL SSL, but I am still can't get it working.
Any help would be much appreciated.
Problem is solved.
I switched back to MySQL 5.6
Re-created CA, server and client certificates with this detailed instruction
Added to my.cnf the client SSL section
[client]
ssl-ca=/etc/pki/mysql_ssl/ca-cert.pem
ssl-cert=/etc/pki/mysql_ssl/client-cert.pem
ssl-key=/etc/pki/mysql_ssl/сlient-key.pem
Created new mysql user
CREATE USER 'x509test'#'%' IDENTIFIED BY 'MyPassword1';
GRANT USAGE ON *.* TO 'x509test'#'%' REQUIRE X509;
FLUSH PRIVILEGES;
And finally I am able to connect
$ mysql --user=x509test --password --ssl-ca=/etc/pki/mysql_ssl/ca-cert.pem --ssl-cert=/etc/pki/mysql_ssl/client-cert.pem --ssl-key=/etc/pki/mysql_ssl/client-key.pem

Digital Certificate: How to import .cer file in to .truststore file using?

Has anyone came across where they have to deal with .truststore file? and knowing how to import .cer into .truststore file?
I am not sure if I have to use Java Keytool or Linux command (such as openssl command).
Thanks
# Copy the certificate into the directory Java_home\Jre\Lib\Security
# Change your directory to Java_home\Jre\Lib\Security>
# Import the certificate to a trust store.
keytool -import -alias ca -file somecert.cer -keystore cacerts -storepass changeit [Return]
Trust this certificate: [Yes]
changeit is the default truststore password
Instead of using sed to filter out the certificate, you can also pipe the openssl s_client output through openssl x509 -out certfile.txt, for example:
echo "" | openssl s_client -connect my.server.com:443 -showcerts 2>/dev/null | openssl x509 -out certfile.txt
The way you import a .cer file into the trust store is the same way you'd import a .crt file from say an export from Firefox.
You do not have to put an alias and the password of the keystore, you can just type:
keytool -v -import -file somefile.crt -alias somecrt -keystore my-cacerts
Preferably use the cacerts file that is already in your Java installation (jre\lib\security\cacerts) as it contains secure "popular" certificates.
Update regarding the differences of cer and crt (just to clarify)
According to Apache with SSL - How to convert CER to CRT certificates? and user #Spawnrider
CER is a X.509 certificate in binary form, DER encoded.
CRT is a binary X.509 certificate, encapsulated in text (base-64) encoding.
It is not the same encoding.