Generating a self-signed cert with openssl that works in Chrome 58
Solution 1:
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
Solution 2:
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
Solution 3:
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
[email protected]
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 [email protected]:~/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
Solution 4:
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
Solution 5:
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, IP:127.0.0.1, IP:::1
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"
google-chrome "https://127.0.0.1:20443"
google-chrome "https://[::1]:20443"
And after testing
kill $openssl_pid