6

I am trying to set up a single SSL certificate that will make any *.local website work over https. I have all .local domains pointing back to my local machine. I use these when developing websites. A lot of new features (geo location, service workers etc.) require an SSL.

I believe that for recent versions of Chrome/Firefox, an old school self-signed certificate no longer works.

Below are the steps I have taken after following a combination of these guides: https://deliciousbrains.com/https-locally-without-browser-privacy-errors/

https://codeghar.wordpress.com/2008/03/17/create-a-certificate-authority-and-certificates-with-openssl/

https://stackoverflow.com/questions/27294589/creating-self-signed-certificate-for-domain-and-subdomains-neterr-cert-commo

Here is my config file:

#..................................
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /home/*****/Sites/root-ca
serial = $dir/serial
database = $dir/index.txt
new_certs_dir = $dir/certs
certificate = $dir/certs/cacert.pem
private_key = $dir/private/cakey.pem
default_days = 3000
default_md = sha256
preserve = no
email_in_dn = no
nameopt = default_ca
certopt = default_ca
policy = policy_match
copy_extensions = copyall
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 2048 # Size of keys
default_keyfile = key.pem # name of generated keys
default_md = md5 # message digest algorithm
string_mask = nombstr # permitted characters
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ req_distinguished_name ]
# Variable name Prompt string
#------------------------- ----------------------------------
0.organizationName = Organization Name (company)
organizationalUnitName = Organizational Unit Name (department, division)
emailAddress = Email Address
emailAddress_max = 40
localityName = Locality Name (city, district)
stateOrProvinceName = State or Province Name (full name)
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
commonName = Common Name (hostname, IP, or your name)
commonName_max = 64
# Default values for the above, for consistency and less typing.
# Variable name Value
#------------------------ ------------------------------
0.organizationName_default = *****
localityName_default = *****
stateOrProvinceName_default = *****
countryName_default = *****
emailAddress_default = *****
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
subjectAltName       = @alternate_names
[ v3_req ]
subjectKeyIdentifier = hash
basicConstraints     = CA:FALSE
keyUsage             = digitalSignature, keyEncipherment
subjectAltName       = @alternate_names
nsComment            = "OpenSSL Generated Certificate"

[ alternate_names ]

DNS.1       = *.local

I first create a new certificate authority:

openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out certs/cacert.pem -days 3000 -config conf/caconfig.cnf

I have given the Common name here as my name

Common Name (hostname, IP, or your name) []:Jonathan Hodgson

The file certs/cacert.pem I then import into chromium's authorities which works without a problem.

I then create a certificate request:

openssl req -extensions v3_req -new -nodes -out local.req.pem -keyout private/local.key.pem -config conf/caconfig.cnf

I have given the Common name here as *.local

Common Name (hostname, IP, or your name) []:*.local

I then sign the request:

openssl ca -out certs/local.cert.pem  -config conf/caconfig.cnf -infiles local.req.pem

I add the files to my http config:

<VirtualHost *:80>
    ServerName test.local
    ServerAlias *.local
    VirtualDocumentRoot /home/jonathan/Sites/%-2/public_html
    CustomLog /home/jonathan/Sites/access.log vhost_combined
    ErrorLog /home/jonathan/Sites/error.log
</VirtualHost>

<VirtualHost *:443>
    ServerName test.local
    ServerAlias *.local
    VirtualDocumentRoot /home/jonathan/Sites/%-2/public_html
    CustomLog /home/jonathan/Sites/access.log vhost_combined
    ErrorLog /home/jonathan/Sites/error.log
    SSLEngine On
    SSLCertificateFile /home/jonathan/Sites/root-ca/certs/local.cert.pem
    SSLCertificateKeyFile /home/jonathan/Sites/root-ca/private/local.key.pem
</VirtualHost>

I have restarted apache but I am still getting NET::ERR_CERT_COMMON_NAME_INVALID

I was under the impression that this was because I needed to add the subjectAltName to the config file which I have done.

Please let me know what I should do differently.

Thanks in advance for any help

Edit

I think the problem is to do with the wildcard. If I set the alternate_names to example.local and the Common name for the request to example.local, example.local shows as secure in both Chrome and Firefox.

I tried to set DNS.1 to local and DNS.2 to *.local, I then just got ERR_SSL_SERVER_CERT_BAD_FORMAT in chrome and SEC_ERROR_REUSED_ISSUER_AND_SERIAL in firefox. I definitely reset my serial file and my index file before generating the certificates.

4 Answers 4

6
+50

You added SAN to the CSR but you didn't tell ca to include extensions from the CSR in the certificate. See https://security.stackexchange.com/questions/150078/missing-x509-extensions-with-an-openssl-generated-certificate or the man page for ca also on the web at copy_extensions

EDIT: You also need to specify x509_extensions in the ca config, or equivalent but less convenient the commandline option -extensions, in either case pointing to a section that exists but can be empty if you don't want any CA-required extensions. I didn't notice this at first because I had never tried the case of extensions from CSR only and not config, which is unrealistic for most CAs. If you specify copy_extensions other than none (and the CSR has some) but don't specify x509_extensions then ca does put the extensions in the cert but does not set the cert version to v3 as is required by standards (like rfc5280) when extensions are present.

It's arguable if this is a bug; the manpage says x509_extensions/extensions controls the v3 setting, and by not saying any similar thing about copy_extensions implies that does not, but IMHO it's certainly a very suboptimal feature. EDIT: it is a bug and will be fixed but until then use the workaround, see https://unix.stackexchange.com/a/394465/59699

HOWEVER: in my test, this didn't actually solve your problem. Even though the cert has *.local in SAN and CN and is (now) otherwise valid, my Firefox (53.0.2) and Chrome (59.0.3071.109) still reject it with SSL_ERROR_CERT_DOMAIN_ERROR and ERR_CERT_COMMON_NAME_INVALID respectively. I guessed they might not be excluding local from the normal 2+-level logic and tried *.example.local: Chrome does accept that, but Firefox doesn't. I also tried *.example.org and both Chrome and IE11 like that but still not Firefox (and of course assigning yourself names in real TLDs like .org is not the way DNS is supposed to work).

This has me stuck. With some work OpenSSL can be made to generate a cert containing almost anything you want, but what Firefox and Chrome will accept I do not know. I will try to look into that and update if I find anything.


I hope you mean you gave *.local as the CommonName only for the server CSR and NOT for the CA (self-signed) cert. If Subject names for CA and leaf certs are the same nothing will work reliably. EDIT: your edited Q confirms they were correctly different. Although it does not mention also specifying Country, State, and Organization as is required by the ca policy you used.

Note 'self-signed' is a term of art and means signed with the same key. Your CA cert is self-signed. Your server cert is signed by you yourself using your own key but it is not self-signed. Trying to apply instructions for a self-signed cert to a not-self-signed cert was part of your problem.

And Gilles point about md5 for the signature algorithm is also correct.

EDIT: 'resetting' serial (and index) for an openssl ca setup is a bad idea, unless you permanently discard the CA cert and name they were used for. The standards say a given CA must not issue more than one cert with the same serial value in the cert, and the serial file is the way openssl ca (and also x509 -req) implements this. 'Real' (public) CAs nowadays no longer use a simple counter but include entropy to block collision attacks on PKI -- google hashclash -- but this is not an issue for a personal CA like yours. I can readily believe a browser (or other relier) being unhappy if it sees multiple certs with the same serial and CA name, although I would NOT expect a browser to persistently store a leaf cert -- and thus see both the old and new ones in one process unless long-running -- unless you import it to the applicable store, including in Firefox if you make it a permanent 'exception'.

6
  • Hi, thanks for your help. I am not entirely sure how I add the copy_extentions. Should this be part of my config file or a flag on one of the commands? I used the common name *.local when generating the request. The Common name for the authority (the first command I ran) I used my name Jun 21, 2017 at 10:47
  • @JonathanHodgson: copy_extensions (s not t, spelling matters!) goes in the config file; that's why it's documented under the subheading 'Configuration File Options'. It goes in the section ca uses directly (not via reference) which in your case is [CA_default] Jun 21, 2017 at 23:03
  • Thanks. I have added copy_extensions = copyall to the [CA_default] section. Re run the commands, restarted apache, re-imported the authority cert into chrome and still have the issue. Thanks for helping Jun 22, 2017 at 9:25
  • thanks for clarifying what a self signed certificate is. Is there anything else I should be changing as a result of my mistake? Jun 22, 2017 at 15:44
  • 1
    @JonathanHodgson: on testing, yes; editing Jun 25, 2017 at 6:36
3

Wildcard certificates are not permitted for a top-level domain like .local or .com and they aren't accepted by Firefox or IE either. https://crbug.com/736715.

0

At least one obvious problem is that you're creating a certificate signed with MD5. MD5 has been deprecated for a while, and broken in certificates specifically since 2012. No reputable server has supported it in a while.

Change default_md md5 to default_md sha256.

If you're following a tutorial that recommends MD5, throw it away, it's too old to be of any use.

Note that I haven't reviewed to see whether there are other problems. The server logs should be helpful.

2
  • Thanks for looking over it. I have changed from md5 to sha256. Re-run all the commands and re-imported the CA certificate into chrome. It's still not working Jun 21, 2017 at 10:41
  • I also re-started apache Jun 21, 2017 at 10:50
0

Neither of the answers worked for me, but I managed to come up with a working Powershell script for my future needs:

#openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout localhost-CA.pem -out localhost-CA.crt -subj '/emailAddress=<redacted>/O=<redacted>/CN=<redacted>/C=US' -extensions v3_ca -addext 'nsCertType=sslCA' # uncomment to generate a root CA cert.
'basicConstraints=CA:FALSE`
extendedKeyUsage=serverAuth`
subjectAltName=IP.1:127.0.0.1,IP.2:::1,DNS:localhost' | Set-Content sign.conf
Invoke-Expression "openssl req -new -sha256 -key localhost.pem -out localhost.csr -subj '/emailAddress=<redacted>/O=<redacted>/CN=<redacted>/C=US' $((Get-Content sign.conf | ForEach-Object { "-addext '$_'" }) -join ' ')"
openssl x509 -req -in localhost.csr -CA localhost-CA.crt -CAkey localhost-CA.pem -CAcreateserial -out localhost.crt -days 3650 -sha256 -extfile sign.conf -extensions default
Remove-Item sign.conf

It passes the same extensions as were used to generate the CSR to the new certificate generation utility w/o having to mess with configuration files, as that to my taste is a bit too much for such a small thing, and the resulting certificate is V3.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .