Skip to content

Instantly share code, notes, and snippets.

@jchandra74
Last active July 12, 2024 09:58
Show Gist options
  • Save jchandra74/36d5f8d0e11960dd8f80260801109ab0 to your computer and use it in GitHub Desktop.
Save jchandra74/36d5f8d0e11960dd8f80260801109ab0 to your computer and use it in GitHub Desktop.
HOWTO: Create Your Own Self-Signed Certificate with Subject Alternative Names Using OpenSSL in Ubuntu Bash for Window

HOWTO: Create Your Own Self-Signed Certificate with Subject Alternative Names Using OpenSSL in Ubuntu Bash for Window

Overview

My main development workstation is a Windows 10 machine, so we'll approach this from that viewpoint.

Recently, Google Chrome started giving me a warning when I open a site that uses https and self-signed certificate on my local development machine due to some SSL certificate issues like the one below:

Self-Signed SSL Issue in Chrome

or one that is described in this forum post which I originally got.

I made my self-signed certificate using MAKECERT utility previously. Apparently, this tool does not support creating self-signed SSL certificate with Subject Alternative Name (SAN). If anyone knows different, please let me know.

So, after doing some searches, it seems that OpenSSL is the best solution for this.

If you are trying to use OpenSSL on Windows like me, you will probably be scratching your head on where to start. Build from the repository? Ouch. That's what they called yak shaving. I just want to quickly create my own damn self-signed certificate, not build a factory that can do that. Sure, there is binary installation available here, but after getting it installed and trying to figure out how to make it run nicely with PowerShell, I gave up.

Luckily, Windows 10 now has the ability to run Ubuntu Bash and after playing around with it, this seems to be the best way forward when using openssl.

Setup Ubuntu on Windows 10

To set it up, follow the instruction here.

Install OpenSSL

To install openssl run the following command from the bash shell:

sudo apt-get install openssl

Once installed, you are ready to create your own self-signed certificate.

Creating Self-Signed Certificate

I am using this OpenSSL Ubuntu article as the base, but there are some modifications along the way, so I'll just explain the way I did it here. If you need further information, please visit that article.

The original article is using SHA1 but we really need to move to something else that is stronger like SHA256. If you are using SHA1 as suggested, you will be getting the Your connection is not private page in Chrome.

Creating Your Working Environment

We will use your user profile root directory (~/ which points to /home/jchandra in my case) to do this work. If you use anything else, you might need to customize the caconfig.cnf and localhost.cnf content below.

To create your environment, run the following in bash:

cd ~/ && mkdir myCA && mkdir -p myCA/signedcerts && mkdir myCA/private && cd myCA

This will create the following directories under your user profile root folder:

Directory Contents
~/myCA contains CA certificate, certificates database, generated certificates, keys, and requests
~/myCA/signedcerts contains copies of each signed certificate
~/myCA/private contains the private key

Create the Certificate Database

To create the database, enter the following in bash:

echo '01' > serial && touch index.txt

Create Certificate Authority Configuration File

Create caconfig.cnf using vim or nano or whatever Linux text-editor of your choice.

To create it using vim, do the following:

    vim ~/myCA/caconfig.cnf

To create it using nano do the following:

    nano ~/myCA/caconfig.cnf

The content should be like so:

# My sample caconfig.cnf file.
#
# Default configuration to use when one is not provided on the command line.
#
[ ca ]
default_ca = local_ca
#
#
# Default location of directories and files needed to generate certificates.
#
[ local_ca ]
dir = /home/jchandra/myCA
certificate = $dir/cacert.pem
database = $dir/index.txt
new_certs_dir = $dir/signedcerts
private_key = $dir/private/cakey.pem
serial = $dir/serial
#
#
# Default expiration and encryption policies for certificates
#
default_crl_days = 365
default_days = 1825
# sha1 is no longer recommended, we will be using sha256
default_md = sha256
#
policy = local_ca_policy
x509_extensions = local_ca_extensions
#
#
# Copy extensions specified in the certificate request
#
copy_extensions = copy
#
#
# Default policy to use when generating server certificates. 
# The following fields must be defined in the server certificate.
#
# DO NOT CHANGE "supplied" BELOW TO ANYTHING ELSE.
# It is the correct content.
#
[ local_ca_policy ]
commonName = supplied
stateOrProvinceName = supplied
countryName = supplied
emailAddress = supplied
organizationName = supplied
organizationalUnitName = supplied
#
#
# x509 extensions to use when generating server certificates
#
[ local_ca_extensions ]
basicConstraints = CA:false
#
#
# The default root certificate generation policy
#
[ req ]
default_bits = 2048
default_keyfile = /home/jchandra/myCA/private/cakey.pem
#
# sha1 is no longer recommended, we will be using sha256
default_md = sha256
#
prompt = no
distinguished_name = root_ca_distinguished_name
x509_extensions = root_ca_extensions
#
#
# Root Certificate Authority distinguished name
#
# DO CHANGE THE CONTENT OF THESE FIELDS TO MATCH
# YOUR OWN SETTINGS!
#
[ root_ca_distinguished_name ]
commonName = InvoiceSmashDev Root Certificate Authority
stateOrProvinceName = NSW
countryName = AU
emailAddress = [email protected]
organizationName = Coupa InvoiceSmash
organizationalUnitName = Development
#
[ root_ca_extensions ]
basicConstraints = CA:true

Caveats for caconfig.cnf:

  1. In [ local_ca ] section, make sure you replace <username> with your Ubuntu username that you created when you setup Ubuntu on Windows 10. Mine for example is dir = /home/jchandra/myCA. NOTE: DO NOT USE ~/myCA. It does not work..
    Similarly, change the default_keyfile setting in [ req ] section to be the same.
  2. Leave the [ local_ca_policy ] section alone. commonName = supplied, etc. are correct and not to be overwritten.
  3. In [ root_ca_distinguished_name ] section, replace all values to your own settings.

Creating Your Test Certificate Authority

  1. Run the following command so openssl will pick the settings automatically:
export OPENSSL_CONF=~/myCA/caconfig.cnf
  1. Generate the Certificate Authority (CA) certificate:
openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 1825
  1. Enter and retype the password you wish to use to import/export the certificate.
    NOTE: Remember this password, you will need it throughout this walk-through.

Once you are done you should have the following files:

File Content
~/myCA/cacert.pem CA public certificate
~/myCA/private/cakey.pem CA private key

In Windows, we will be using .crt file instead, so create one using the following command:

openssl x509 -in cacert.pem -out cacert.crt

Creating Your Self-Signed Certificate with Subject Alternative Name (SAN)

Now that you have your CA, you can create the actual self-signed SSL certificate.

But first, we need to create the configuration file for it. So again, use vim or nano, etc. to create the file. In this example, I will call mine localhost.cnf since that's the server that I am going to be using to test my development code. You can call it whatever you want. Just make sure you use the right filename in the export command later on.

Below is the content of ~/myCA/localhost.cnf:

#
# localhost.cnf
#

[ req ]
prompt = no
distinguished_name = server_distinguished_name
req_extensions = v3_req

[ server_distinguished_name ]
commonName = localhost
stateOrProvinceName = NSW
countryName = AU
emailAddress = [email protected]
organizationName = Coupa InvoiceSmash
organizationalUnitName = Development

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[ alt_names ]
DNS.0 = localhost
DNS.1 = invoicesmash.local

Caveats for localhost.conf

  1. Change the values in [ server_distinguished_name ] section to match your own settings.
  2. In [ alt_names ] section, change the value for DNS.0 and DNS.1 to whatever you need. In my case, I test my web application using https://localhost:44300, therefore the correct value for me is DNS.0 = localhost. I am not sure what to do with DNS.1 so, I just changed it to DNS.1 = invoicesmash.local. If so happen that I have a host entry in my hosts file that matches this (mapped to IP Address 127.0.0.1), it should still work.

Once you created the configuration file, you need to export it:

export OPENSSL_CONF=~/myCA/localhost.cnf

Now generate the certificate and key:

openssl req -newkey rsa:2048 -keyout tempkey.pem -keyform PEM -out tempreq.pem -outform PEM

Again, provide the password that you previously entered and wait for the command complete.

Next, run the following to create the unencrypted key file:

openssl rsa < tempkey.pem > server_key.pem

Again, provide the password that you previously entered and wait for the command to be completed.

Now switch back the export to caconfig.cnf so we can sign the new certificate request with the CA:

export OPENSSL_CONF=~/myCA/caconfig.cnf

And sign it:

openssl ca -in tempreq.pem -out server_crt.pem

Again, provide the password that you previously entered and wait for the command to be completed and just type in Y whenever it asks you for [y/n].

Now you should have your self-signed certificate and the key.

File Content
~/myCA/server_crt.pem Self signed SSL certificate
~/myCA/server_key.pem Self signed SSL certificate private key

Converting the .pem files to .pfx for usage by Windows

In Windows, we mostly use .pfx and .crt files. Therefore, we need to convert the .pem file to .pfx. We'll use cat to combine server_key.pem and server_crt.pem into a file called hold.pem. Then we will do the conversion using openssl pkcs12 command as shown below. You can use whatever text you want to describe your new .pfx file in the -name parameter.

cat server_key.pem server_crt.pem > hold.pem
openssl pkcs12 -export -out localhost.pfx -in hold.pem -name "InvoiceSmash Dev Self-Signed SSL Certificate"

Again, provide the password that you previously entered and wait for the command to be completed.

Now you should have the following files that we will use in the next section.

File Content
~/myCA/localhost.pfx Self signed SSL certificate in PKCS#12 format
~/myCA/cacert.crt CA certificate used to signed the self-signed certificate

Copy the PFX and CA Certificate to a Windows location and Install the CA & PFX into Windows

Copying PFX and CA from Ubuntu bash to Windows Side

It seems it is forbidden to touch the Linux Subsystem from Windows side, but you can touch Windows side from Linux side, so that's what we are going to do.

To copy the files from inside Ubuntu, you need to know where you want to copy the files to on Windows side. For example, if I want to copy the files to C:\certificates folder, I'd do something like cp {localhost.pfx,cacert.crt} /mnt/c/certificates.

See this faq if you want to know more about this.

Install the new CA and self-signed certificates

To install the CA and self-signed certificates, all you need to do is double-click the file from where you copied them into.

Once clicked, just follow the Install Certificate steps and you should be good.

For the CA Certificate (`cacert.crt), make sure you install it to Local Machine, Trusted Root Certification Authorities.

For the self-signed certificate (localhost.pfx), install it to Local Machine, enter the password as previously, and store it in Personal.

That's it. Now you can configure your application to use the new certificate. In my situation, I just need to configure the Azure Cloud Service project to use that certificate as pointed by this document. I do not know your workflow, so it might be different.

References

@martincostello
Copy link

Excellent guide, thanks!

@sergeyleyko
Copy link

great! thank you

@OmarWKH
Copy link

OmarWKH commented Aug 15, 2017

Thanks! This really helped. For any future visitors:

  • Make the information in [root_ca_distinguished_name] and [server_distinguished_name] in the config file are different from each other. As discussed here.
  • You might need to add IP.0 = 127.0.0.1 to [ alt_names ] (discussed here).

@SeriousM
Copy link

Dear @jchandra74, thank you very much.
Your guide was very good explained, well crafted and easy to follow.
Your're the real MVP :)

@ds00424
Copy link

ds00424 commented Nov 17, 2017

Very nice. Thank you for this. Very helpful.
I did, however, need to change the localhost.cnf slightly.
(Since I am doing this on my local dev box, I only have an IP address -- no domain name,)
Instead of "subjectAltName = @alt_names", I used "subjectAltName = IP:My.IP.Addr.Here"
I commented the entire "[ alt_names ]" section.

With this change Chrome stopped complaining about the cert.

@belvederek837
Copy link

Thanks so much @jchandra74 !!
I followed the steps but encountered the following errors after running the command

openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 1825

image

Tried asking Google but didn't find much info... appreciate your advice!! Thanks a million!!

@tyrannosaurusjames
Copy link

@belvederek837 you might have already sorted this out but that error means you have a typo in your config file - specifically organizationUnitName should probably be organizationalUnitName (I did the same thing when working through this)

@chx
Copy link

chx commented Mar 18, 2018

Instead of exporting you could just use -config caconfig.cnf . Also, adding encrypt_key = no to [ req ] makes life easier.

@ogn4621
Copy link

ogn4621 commented Jul 19, 2018

I just followed this guide, its great thank you! Please add these tasks as a warning pops up during cert signing:

  1. Create attr file for index.txt with: echo '01' > serial && touch index.txt.attr
  2. Open index.txt.attr and add the text "unique_subject = yes/no". Save the file and then sign the cert.

@TomerHeber
Copy link

TomerHeber commented Oct 17, 2018

Great step by step guide. Thank you.

@rebeccakuba
Copy link

Thank you so much. I was so tired of working with FF instead!

@harrymendez23
Copy link

This is GOLD! Thank you very much for putting this together!

@kumakichi
Copy link

Thank you for you fancy work, I just put all the code snippets in this document into a single bash script file: https://gist.github.com/kumakichi/c4476d32b44c0c50043a80a9b771086d

@lperamo
Copy link

lperamo commented Jan 26, 2019

Thanks for this amazing guide...but i have just one problem :

nginx: [warn] "ssl_stapling" ignored, issuer certificate not found for certificate "C:/LPAMP/www/projets-perso/Particles/cert/server_crt.pem"

What could be wrong ? 🤔

@phivid
Copy link

phivid commented Mar 15, 2019

BRAVO !

@SapientHetero
Copy link

Thanks, this post really helped me get started. For others creating certs for an embedded system like I am, you may want to consider using an elliptic curve key rather than RSA. It's MUCH faster and the key is smaller.

@ozanmuyes
Copy link

ozanmuyes commented May 11, 2019

I'm on Windows 7 and Git Bash always stucks at "Enter PEM pass phrase" step (without showing it), so interactive prompts will not work. To overcome this I created a cmd (Windows batch) file (name doesn't matter - but sake of the example say ccak) which reads;

REM File ccak.cmd at C:/Users/USERNAME/myCA

SET OPENSSL_CONF=C:/Users/USERNAME/myCA/caconfig.cnf
openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 1825

and after that added an alias to '~/.bashrc';

alias ccak='winpty ~/myCA/ccak.cmd'

To alias to take effect on the current Git Bash window, run;

. ~/.bashrc

to source the .bashrc file.

After those intermediary steps I was able to ran

openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 1825

command from Creating Your Test Certificate Authority section.

PS: Same procedures goes for all other interaction-needy openssl commands.

PPS: Do NOT forget to change USERNAME with your own username on Windows.

@jnbutler1815
Copy link

jnbutler1815 commented Jul 31, 2020

I exported the env variable OPENSSL_CONF to the correct path to caconfig.conf and then run the command
openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 365
and get this error message:
139984606225728:error:0E079065:configuration file routines:def_load_bio:missing equal sign:../crypto/conf/conf_def.c:391:line 1
139984606225728:error:0E079065:configuration file routines:def_load_bio:missing equal sign:../crypto/conf/conf_def.c:391:line 1

I am running this on a Homestead VM running Ubuntu 20.04. Box has all updates and the latest version of openssl.
The host is Windows 10.

EDIT: I had better luck generating a crt and key on Ubuntu using these steps.

  1. export OPENSSL_CONF="path to the ubuntu openssl cnf file"

  2. Create this cert.conf file with this content :
    `
    [ req ]

    default_bits = 2048
    default_keyfile = server-key.pem
    distinguished_name = subject
    req_extensions = req_ext
    x509_extensions = x509_ext
    string_mask = utf8only

    [ subject ]

    countryName = Country Name (2 letter code)
    countryName_default = US

    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = NY

    localityName = Locality Name (eg, city)
    localityName_default = New York

    organizationName = Organization Name (eg, company)
    organizationName_default = Example, LLC

    commonName = Common Name (e.g. server FQDN or YOUR name)
    commonName_default = homestead.test

    emailAddress = Email Address
    emailAddress_default = [email protected]

    [ x509_ext ]

    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer

    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    subjectAltName = @alternate_names
    nsComment = "OpenSSL Generated Certificate"

    [ req_ext ]

    subjectKeyIdentifier = hash

    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    subjectAltName = @alternate_names
    nsComment = "OpenSSL Generated Certificate"

    [ alternate_names ]

    DNS.1 = homestead.test
    `

NOTE: you have to change the subject area and the DNS.1 to your values.

  1. execute this command:
    openssl req -config cert.conf -new -sha256 -newkey rsa:2048 -nodes -keyout "yourhostname".key -x509 -days 365 -out "yourhostname".crt

  2. place your crt and key file in the apache or nginx folders.

  3. Install the crt on the Windows host machine in the Trusted Certificate Authorities area.

  4. Restart browser and see screenshot for the secured certificate designated by the lock symbol.

ScreenShot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment