As of Chrome 58 it no longer accepts self-signed certs that rely on Common Name: https://productforums.google.com/forum/#!topic/chrome/zVo3M8CgKzQ;context-place=topicsearchin/chrome/category$3ACanary%7Csort:relevance%7Cspell:false

Instead it requires using Subject Alt Name. I have been previously following this guide on how to generate a self-signed cert: https://devcenter.heroku.com/articles/ssl-certificate-self which worked great because I required the server.crt and server.key files for what I'm doing. I now need to generate new certs that include the SAN however all of my attempts to do so have not worked with Chrome 58.

Here is what I've done:

I followed the steps on the above mentioned Heroku article to generate the key. I then wrote a new OpenSSL config file:

[ req ]
default_bits        = 2048
distinguished_name  = req_distinguished_name
req_extensions      = san
extensions          = san
[ req_distinguished_name ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com

Then generated the server.crt with the following command:

openssl req \
-new \
-key server.key \
-out server.csr \
-config config.cnf \
-sha256 \
-days 3650

I'm on a Mac, so I opened the server.crt file with Keychain, added it to my System Certificates. I then set it to Always Trust.

With the exception of the config file to set the SAN value these were similar steps I used in prior versions of Chrome to generate and trust the self-signed cert.

However, after this I still get the ERR_CERT_COMMON_NAME_INVALID in Chrome 58.

up vote 51 down vote accepted

My solution:

openssl req \
    -newkey rsa:2048 \
    -x509 \
    -nodes \
    -keyout server.key \
    -new \
    -out server.crt \
    -subj /CN=dev.mycompany.com \
    -reqexts SAN \
    -extensions SAN \
    -config <(cat /System/Library/OpenSSL/openssl.cnf \
        <(printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com')) \
    -sha256 \
    -days 3650

Status: Works for me

  • 2
    great use of subshell. I think you can simplify it a bit: -config <(cat /System/Library/OpenSSL/openssl.cnf ; printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com') – jrwren Apr 21 '17 at 16:45
  • 1
    I no longer get the Subject Alt Name error, but now I get an error about the common name, and setting the downloaded cert to "always trust" does not work. Any thoughts? @bcardarella – rugbert May 25 '17 at 20:11
  • 2
    With update to Chrome 59, the certificate shows an error like this : There are issues with the site's certificate chain (net::ERR_CERT_COMMON_NAME_INVALID). – theHarsh Jun 17 '17 at 10:51
  • 1
    I changed dev.company.name to localhost and this worked for serving local development site from localhost. On macOS I also had to add the certificate to Keychain and set SSL to "Always Trust". – Daniel M. Nov 9 '17 at 17:52
  • 1
    That's by far the simplest solution and doesn't require screwing around with sslconf or install a CA. – bp. Jan 25 at 8:57

In Windows, save this script in your SSL folder as makeCERT.bat. It will create these files: example.cnf, example.crt, example.key

@echo off

REM IN YOUR SSL FOLDER, SAVE THIS FILE AS: makeCERT.bat
REM AT COMMAND LINE IN YOUR SSL FOLDER, RUN: makecert
REM IT WILL CREATE THESE FILES: example.cnf, example.crt, example.key
REM IMPORT THE .crt FILE INTO CHROME Trusted Root Certification Authorities
REM REMEMBER TO RESTART APACHE OR NGINX AFTER YOU CONFIGURE FOR THESE FILES

REM PLEASE UPDATE THE FOLLOWING VARIABLES FOR YOUR NEEDS.
SET HOSTNAME=example
SET DOT=com
SET COUNTRY=US
SET STATE=KS
SET CITY=Olathe
SET ORGANIZATION=IT
SET ORGANIZATION_UNIT=IT Department
SET EMAIL=webmaster@%HOSTNAME%.%DOT%

(
echo [req]
echo default_bits = 2048
echo prompt = no
echo default_md = sha256
echo x509_extensions = v3_req
echo distinguished_name = dn
echo:
echo [dn]
echo C = %COUNTRY%
echo ST = %STATE%
echo L = %CITY%
echo O = %ORGANIZATION%
echo OU = %ORGANIZATION_UNIT%
echo emailAddress = %EMAIL%
echo CN = %HOSTNAME%.%DOT%
echo:
echo [v3_req]
echo subjectAltName = @alt_names
echo:
echo [alt_names]
echo DNS.1 = *.%HOSTNAME%.%DOT%
echo DNS.2 = %HOSTNAME%.%DOT%
)>%HOSTNAME%.cnf

openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -keyout %HOSTNAME%.key -days 3560 -out %HOSTNAME%.crt -config %HOSTNAME%.cnf

Here is a solution that works for me:

Create CA key and cert

# openssl genrsa -out server_rootCA.key 2048
# openssl req -x509 -new -nodes -key server_rootCA.key -sha256 -days 3650 -out server_rootCA.pem

Create server_rootCA.csr.cnf

# server_rootCA.csr.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=DE
ST=Berlin
L=NeuKoelln
O=Weisestrasse
OU=local_RootCA
emailAddress=ikke@server.berlin
CN = server.berlin

Create v3.ext configuration file

# v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = server.berlin

Create server key

# openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <( cat server_rootCA.csr.cnf )

Create server cert

# openssl x509 -req -in server.csr -CA server_rootCA.pem -CAkey server_rootCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile v3.ext

Add cert and key to Apache2 site-file, HTTPS (port 443) section

SSLCertificateFile    /etc/apache2/ssl/server.crt
SSLCertificateKeyFile    /etc/apache2/ssl/server.key

Copy server_rootCA.pem from the server to your machine..

# scp you@server.berlin:~/server_rootCA.pem .

.. and add it to Chromium browser

Chromium -> Setting -> (Advanced) Manage Certificates -> Import -> 'server_rootCA.pem'

YOU ARE ALL DONE!

P.S. Instead of creating a functional CA & server cert pair (per the instructions above) you could simply disable HSTS headers in your HTTP server config. This will prevent Chromium from enforcing HTTPS and will allow users to click “Advanced → proceed to your.url (unsafe)” without having to obtain and install your custom CA (server_rootCA.pem) certificate. In other words – having to disable HSTS will allow your site to be publicly viewed over HTTP and/or insecure HTTPS connection (beware!).

For Apache2 add the following to site-file, HTTP (port 80) section

Header unset Strict-Transport-Security
Header always set Strict-Transport-Security "max-age=0;includeSubDomains"

Tested on Debian/Apache2.4 + Debian/Chromium 59

https://ram.k0a1a.net/self-signed_https_cert_after_chrome_58

  • Going the route of a root CA authority which later signs the individual certs is the only way that I could get chrome to fully authenticate; also has the advantage that I only needed to get people to install a single cert. Thanks – geoff Dec 4 '17 at 16:18
  • 4
    Can someone please explain to me why everyone in this area seems to use bashisms like -config <( cat server_rootCA.csr.cnf ) instead of just -config server_rootCA.csr.cnf? – Caesar Mar 28 at 7:49
  • can you please update your answer relating to the apache headers that can bypass the issue (I dont mind this is for local sites for development only and I would ike a generic soltuion without having to generate new certificates each time). Can you point out where inside a virtual host defintion these should go. I have tried several alternatives and still cannot access the sites thorugh https. Thanks – Nikos M. 5 hours ago

There are several great answers that give examples of how to get this working, but none that explain where things went wrong in your attempt. OpenSSL can be pretty non-intuitive some times so it is worth walking through.

First, as an aside, OpenSSL defaults to ignoring any distinguished name values you provide in the config. If you want to use them you must add prompt = no to your config. In addition, the command as written only generates a certificate request not a certificate itself, so the -days command does nothing.

If you generate your certificate request using this command you gave and inspect the result, the Subject Alt Name is present:

$ openssl req -new -key server.key -out server.csr -config config.cnf -sha256
$ openssl req -text -noout -in server.csr
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name:
                DNS:dev.mycompany.com
    Signature Algorithm: sha256WithRSAEncryption
         ...

But then if you generate the certificate using the command in heroku link and inspect the result, the Subject Alt Name is missing:

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
$ openssl x509 -text -noout -in server.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            89:fd:75:26:43:08:04:61
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Validity
            Not Before: Jan 21 04:27:21 2018 GMT
            Not After : Jan 21 04:27:21 2019 GMT
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         ...

The reason is that by default OpenSSL does not copy extensions from the request to the certificate. Normally, the certificate would be created/signed by a CA based on a request from a customer, and some extensions could grant the certificate more power than the CA was intending if they were to blindly trust the extensions defined in the request.

There are ways to tell OpenSSL to copy the extensions, but IMHO it is more work than just providing the extensions in a config file when you generate the certificate.

If you were to attempt to use your existing config file, it won't work because the top level section is marked [req] so those settings only apply to the req command not the x509 command. It isn't necessary to have a top-level section marker, so you can just remove that first line, and then it will work fine for both generating requests or certificate.

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt -extfile config.cnf

Alternately, you can use the -x509 argument to the req command to generate a self-signed certificate in a single command, rather than first creating a request and then a certificate. In this case it isn't necessary to remove the [req] section line, as that section is read and used by the req command.

$ openssl req -x509 -sha256 -days 365 -key server.key -out server.crt -config config.cnf

To recap, here is the modified config file used in the above commands:

default_bits        = 2048
distinguished_name  = dn
x509_extensions     = san
req_extensions      = san
extensions          = san
prompt              = no
[ dn ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com
  • This is the only explanation that helped me understand why the certificate came out without a SAN (in my case, I needed to include x509_extensions in the config file) – Daniel Beardsmore Dec 10 at 9:54

My solution is to keep the main openssl.cnf like it is and just at the end to add a new section like [ cert_www.example.com ] where www.example.com is the website for which I want to create a certificate, and in it, put the subjectAltName I would need (and anything else). Of course the section could be named whatever you want.

After that, I can run the openssl req command as before, just adding -extensions cert_www.example.com for its content to be picked up and I add -subj to add directly all the DN info.

Do not forget to verify the certificate content after its creation and before its use, with openssl x509 -text

Bash script with config baked in

As a shell script that should work across platforms with bash. Assumes HOSTNAME env set for the shell or supply a hostname of your choosing, e.g. self_signed_cert.sh test

set -e

if [ -z "$1" ]; then
  hostname="$HOSTNAME"
else
  hostname="$1"
fi

local_openssl_config="
[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = san_self_signed
[ req_distinguished_name ]
CN=$hostname
[ san_self_signed ]
subjectAltName = DNS:$hostname, DNS:localhost
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
extendedKeyUsage = serverAuth, clientAuth, timeStamping
"

openssl req \
  -newkey rsa:2048 -nodes \
  -keyout "$hostname.key.pem" \
  -x509 -sha256 -days 3650 \
  -config <(echo "$local_openssl_config") \
  -out "$hostname.cert.pem"
openssl x509 -noout -text -in "$hostname.cert.pem"

The above more or less injects the bare minimum config file info openssl needs.

Note, included extra DNS:localhost as a SAN to allow testing via localhost more easily. Remove that extra bit from the script if you don't want it.

Credit

bcardarella's answer is great (can't comment/up-vote due insufficient rep). However, the answer uses an existing openssl config file location that is platform specific... hence:

Works for me

Obviously, one would simply need to find the openssl config file for your own given platform and substitute the correct location.

Test

For a way to test, import test.cert.pem into chrome's authorities in chrome://settings/certificates, and:

openssl s_server -key test.key.pem -cert test.cert.pem -accept 20443 -www &
openssl_pid=$!
google-chrome https://localhost:20443

And after testing

kill $openssl_pid

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.