Difference between revisions of "Openssl - Generate Self-signed Certificate for Chrome"

From TheBeard Science Project Wiki
Jump to: navigation, search
(Concatenating the Key and Certificate)
(Concatenating the Key and Certificate)
 
(7 intermediate revisions by the same user not shown)
Line 77: Line 77:
 
=== Alternative Names ===
 
=== Alternative Names ===
  
Notice the <code>[&nbsp;v3_ca_with_san&nbsp;]</code> extension I created is slightly modified, and includes the <code>subjectAltName</code> value which refers to another extension that I called <code>[&nbsp;alt_names&nbsp;]</code>. The <code>[&nbsp;alt_names&nbsp;]</code> extension then defines several DNS names. You <b>must</b> include the Common Name (ie. server.mydomain.local) as an Alternative Name. After that you can add any other alternative names you want, and even IP addresses. Be sure to number them as seen below.
+
Notice the <code>[&nbsp;v3_ca_with_san&nbsp;]</code> extension I created (below) is slightly modified, and includes the <code>subjectAltName</code> value which refers to another custom extension that I named <code>[&nbsp;alt_names&nbsp;]</code>. The <code>[&nbsp;alt_names&nbsp;]</code> extension then defines several DNS names. You <b>must</b> include the Common Name (ie. server.mydomain.local) as an Alternative Name. After that you can add any other alternative names you want, and even IP addresses. Be sure to number them as seen below.
  
 
=== Hash Algorithm ===
 
=== Hash Algorithm ===
  
Lastly, we are including the <code>-sha256</code> parameter to ensure we are using the minimum strength hash algorithm that is accepted in Chrome. You could also use <code>-sha512</code>.
+
Lastly, we are including the <code>-sha256</code> parameter to the <code>openssl</code> command to ensure we are using the minimum strength hash algorithm that is accepted in Chrome. You could also use <code>-sha512</code>.
  
 
=== Checklist ===
 
=== Checklist ===
Line 88: Line 88:
 
* You already have a private key called <code>server.key</code>
 
* You already have a private key called <code>server.key</code>
 
* You want the output certificate to be saved to <code>server.crt</code>
 
* You want the output certificate to be saved to <code>server.crt</code>
* You've edited <b>ssl_config</b> variable to the correct location of you <code>openssl.cnf</code> file on your system
+
* You've edited <b>ssl_config</b> variable in this script to the correct location of your <code>openssl.cnf</code> file on your system
* Your Subject Common Name is valid (must have domain name, ie. "server.mydomain.local")
+
* Your <b>Subject Common Name</b> is valid (must have domain name, ie. "server.mydomain.local")
* You've added all The Alternative Names you want to use
+
* You've added all the <b>Alternative Names</b> you want to use
* You've edited/added Organization name, email, etc. to reflect your needs
+
* You've edited/added organization name, email, etc. to reflect your needs
  
 
=== <span id="thescript"></span>The Script ===
 
=== <span id="thescript"></span>The Script ===
Line 160: Line 160:
 
=== Concatenating the Key and Certificate ===
 
=== Concatenating the Key and Certificate ===
  
This is not necessary in most cases, but I've run into web applications that expect the key and the certificate to be contained in a single file (this does not cause the application tosend the private key to end users). Simply combine them into one file with the key at the top.
+
This is not necessary in most cases, but I've run into web applications that expect the key and the certificate to be contained in a single file (this does not cause the application to send the private key to end users). Simply combine them into one file with the key at the top.
  
 
<source lang="shell">
 
<source lang="shell">

Latest revision as of 01:25, 13 June 2019

Introduction

After the release of Google Chrome 58, self-signed certificates were no longer working in Chrome as they once did. Google started enforcing new security rules and this made Chrome consider many certificates invalid.

In my case, the problems I encountered were:

  • The Subject Common Name was invalid NET::ERR_COMMON_CERT_INVALID
  • The cert did not have a Subject Alternative Name Developer tools said "Subject Alternative Name Missing"
  • The cert used a weak algorithm NET::ERR_CERT_WEAK_SIGNATURE_ALGORITHM
  • Importing a cert into Chrome as a server certificate no longer worked


Why is this a problem?

I access my personal servers a lot, and I use self-signed certificates. Before Chrome 58, I could import the certificate as a server cert, and this would bypass the SSL error screen and would display a secure lock icon with green text on the address bar. This saved me the time of clicking through the error, but the lock icon also told me that I'm accessing my server. If someone man-in-the-middle attacked me I would get a certificate error, thus giving me a blatant indicator that something is wrong.

I searched a long time for a solution, but I abandoned the effort for a while. For about 6 months, I was seeing an SSL error every time I accessed my servers. Now since I see an SSL error all the time, if someone attacked me I wouldn't notice. "Thanks Google...", I thought, "...for making your browser more secure for everyone except me." I can't complain about them adding security features, but not providing any guides or documentation that helps the average Joe Techie generate valid self-signed certificates, it's just cruel.

The following section describes the process that worked for me.

Note: I'm using OpenSSL 1.1.0g 2 Nov 2017

Generating an RSA Key

The rest of this tutorial assumes you already have an RSA key. If you don't already have a key, the following command will generate a new 2048 bit RSA key that is not locked with a password (so your web server can start without human intervention).

openssl genrsa -out server.key 2048

Generating a CA Cert

I'm assuming you're using openssl on Linux (or MacOS) in this tutorial. I've simplified things using a bash script, but if you're doing this on a Windows platform, you can simply create a custom openssl.cnf file that has the added lines that I create in my script.

In my case, I'm using this cert for my Apache server. I'm going to backup my old certificates, cd into the folder where I keep the certs, and create a shell script (do these steps as root):

cd /etc/apache2/
cp -r ssl ssl.orig
cd ssl
touch make-cert.sh
chmod u+x make-cert.sh

Before we continue, we need to understand some things.

Openssl Config File

Openssl uses a default config file called openssl.cnf. Different systems have this file in different locations. If you can't find it, search for it:

find / -type f -name openssl.cnf

Replace the ssl_config variable in the bash script below with the correct openssl.cnf file location for your system.

If you take a look at the contents of openssl.cnf you will see a section like this:

[ v3_ca ]

# ..several comments..

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = CA:true

This is the magic that makes our cert a CA cert. We can add this as an extension, but we are going to create a custom version of v3_ca so we can include multiple Subject Alternative Names.

Organization Name

Notice in the script below that I've chosen "AAA" as the Organization Name (/O=AAA) and Organizational Unit (/OU=AAA). This was just to make the cert appear at the top of the cert list in Chrome. It was faster for troubleshooting. You can choose any name you want.

Common Name

Your Subject Common Name (/CN=name) needs to be valid. Chrome no longer considers a simple hostname a valid CN. You need to include a domain name. In my example, because I'm using this cert for accessing my server from my local network (I use a different cert for public access), it's sufficient to use server.mydomain.local.

Alternative Names

Notice the [ v3_ca_with_san ] extension I created (below) is slightly modified, and includes the subjectAltName value which refers to another custom extension that I named [ alt_names ]. The [ alt_names ] extension then defines several DNS names. You must include the Common Name (ie. server.mydomain.local) as an Alternative Name. After that you can add any other alternative names you want, and even IP addresses. Be sure to number them as seen below.

Hash Algorithm

Lastly, we are including the -sha256 parameter to the openssl command to ensure we are using the minimum strength hash algorithm that is accepted in Chrome. You could also use -sha512.

Checklist

You are almost ready to create your script. Here's a checklist of assumptions for this script:

  • You already have a private key called server.key
  • You want the output certificate to be saved to server.crt
  • You've edited ssl_config variable in this script to the correct location of your openssl.cnf file on your system
  • Your Subject Common Name is valid (must have domain name, ie. "server.mydomain.local")
  • You've added all the Alternative Names you want to use
  • You've edited/added organization name, email, etc. to reflect your needs

The Script

Now edit the make-cert.sh script and add these contents (modify to meet your needs):

#!/bin/bash
#
# Generate CA certificate from a pre-existing key that works with Chrome 58+.
#

input_key=server.key
output_crt=server.crt
ssl_config=/usr/lib/ssl/openssl.cnf

ssl_subj=$(cat <<EOF
/C=US
/ST=California
/L=SomeCity
/emailAddress=root@server.mydomain.local
/O=AAA
/OU=AAA
/CN=server.mydomain.local
EOF
)

ssl_custom=$(cat <<EOF
[ v3_ca_with_san ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
subjectAltName = @alt_names
[alt_names ]
DNS.1 = server
DNS.2 = www
DNS.3 = server.mydomain.local
DNS.4 = www.mydomain.local
IP.1  = 192.168.0.2
EOF
)

openssl req \
    -key "$input_key" \
    -x509 \
    -nodes \
    -new \
    -out "$output_crt" \
    -subj $(echo -n $ssl_subj|tr -d ' ') \
    -reqexts 'v3_ca_with_san' \
    -extensions 'v3_ca_with_san' \
    -config <(cat "$ssl_config" \
            <(printf "$ssl_custom")) \
    -sha256 \
    -days 10000

# Restart Apache. I did this for quick testing. You don't need to include this.
systemctl restart apache2

Read through this script to understand what it's doing. Now run the script:

./make-cert.sh

If there was no output, it likely completed successfully. There should be a server.crt file and your Apache server be restarted. You can now import the certificate into Chrome.

Concatenating the Key and Certificate

This is not necessary in most cases, but I've run into web applications that expect the key and the certificate to be contained in a single file (this does not cause the application to send the private key to end users). Simply combine them into one file with the key at the top.

cp server.crt server.crt.orig
cat server.key server.crt.orig > server.crt

Sometimes it's expected that the certificate file have a .pem extension instead of .crt, but usually either will work.

Importing the Certificate into Chrome

Open Chrome and navigate to your site via HTTPS (ie. https://server.mydomain.local). You will now see the following SSL error.

Ssl01.jpg


Open the Developer tool by pressing F12 or Shift + Ctrl + I. Click the Security tab and click View Certificate.

Ssl02.jpg


In the Certificate Viewer click the Details tab. Look through the details to make sure you are, in fact, recieving the correct certificate. Click the Export... button at the bottom.

Ssl03.jpg


Save the file as a Base64-encoded ASCII, single certificate and be sure to add a .pem extension to the file name. For some reason, Chrome doesn't export the cert with an extension, but when you import a cert it expects an extension. Logical, eh?

Ssl04.jpg


Go to the main menu in Chrome and click Settings. Scroll to the bottom and click Advanced. Under the Privacy and Security section click on Manage Certificates.

Ssl05.jpg


Click on the Authorities tab and click IMPORT. Locate the server.mydomain.local.pem file you exported and click Open.

Ssl06.jpg


Check all three boxes and click Ok. You should be able to scroll down the Authorities list and find your certificate in the list.

Ssl07.jpg


Close all Chrome windows and open it again. You should now be able to navigate to your site via HTTPS. No SSL errors and a nice, green lock icon. Success!!

Ssl08.jpg

Importing the Certificate into Android

I found that Chrome on Android does not have the option to export a certificate the same way as before. Also, you don't import the cert into the browser, you import it into the operating system's credential storage. Also consider that some Android devices are different, so your process may differ from mine.


First, download the certificate to your SD card. I just attached my phone via USB to my computer and transferred the PEM file.

Go to Settings and click on the General tab. Then click on Security.

Ssl09.png


Go to Certificate Management and click on Install from Storage.

Ssl10.png


Locate the cert file you saved and click it. You will be asked to confirm your PIN (or lock pattern, fingerprint, etc.).

Ssl11.png


Now enter your Certificate name. This is just a superficial name; I used the server name. Everything else you can leave as it is. Now click Ok.

Ssl12.png


You should now be able to open your browser (close any existing tabs) and navigate to your server via HTTPS without any SSL errors.