DEV Community

Cover image for How to create a self-signed certificate
suntong
suntong

Posted on

How to create a self-signed certificate

Prefix

All previous squid caching server series talk about SSL bumping, and include steps on creating a self-signed certificate. I.e., it has been overly-discussed --

  • You can find many other blog/articles talking about this all over the Internet. Besides,
  • There is a question on stackoverflow with over a thousand up votes.

So why another one?

  • First, many such blog/articles just talk about it, giving some commands but without explaining the parameters in details (just like the first two articles we covered in this series). E.g., the comment -nodes doesn't mean "nodes" have received over four hundred up-votes before the explanation is added to the above accepted answer.
  • You will find that each of them, of all those blog/articles on the Internet, are doing it a little bit differently. So why the differences? And again, is any of them outdated?
  • And most importantly, the accepted answer is not the only answer there, and if you scroll down, you'd realized that there are much more things to be cautious on than the accepted answer. For example,
  • "Modern browsers now throw a security error for otherwise well-formed self-signed certificates ..., so many developers' tutorials and bookmarks are suddenly outdated." A comment can be easily missed because it is buried within all the other answers and resides at the very low part of them, you have to scroll a long way to get to it.

This is part of my fact-collecting blog of the squid caching server series, extending on the creating self-signed certificate part, which combines the image & writing from Linode's Create a Self-Signed TLS Certificate and the answers from above stackoverflow question.

What is a Self-Signed TLS Certificate?

Self-signed TLS certificates are suitable for personal use or for applications that are used internally within an organization. If you intend to use your SSL certificate on a website, see our (linode.com) guide on enabling TLS for NGINX once you’ve completed the process outlined in this guide.

Create the Certificate - Getting Started

Again, reposting from linode's article,

  1. Change to the root user and change to the directory in which you want to create the certificate and key pair. That location will vary depending on your needs. Here we'll use /root/certs:

    su - root
    mkdir /root/certs && cd /root/certs
    
  2. Create the certificate:

    openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out MyCertificate.crt -keyout MyKey.key
    

You will be prompted to add identifying information about your website or organization to the certificate. Since a self-signed certificate won't be used publicly, this information isn't necessary. If this certificate will be passed on to a certificate authority for signing, the information needs to be as accurate as possible.

The following is a breakdown of the OpenSSL options used in this command. There are many other options available, but these will create a basic certificate which will be good for a year. For more information, see man openssl in your terminal.

  • -newkey rsa:4096: Create a 4096 bit RSA key for use with the certificate. RSA 2048 is the default on more recent versions of OpenSSL but to be sure of the key size, you should specify it during creation.
  • -x509: Create a self-signed certificate.
  • -sha256: Generate the certificate request using 265-bit SHA (Secure Hash Algorithm).
  • -days: Determines the length of time in days that the certificate is being issued for. For a self-signed certificate, this value can be increased as necessary.
  • -nodes: Create a certificate that does not require a passphrase. If this option is excluded, you will be required to enter the passphrase in the console each time the application using it is restarted.
  1. Restrict the key's permissions so that only root can access it:

    chmod 400 /root/certs/MyKey.key
    
  2. Back up your certificate and key to external storage. This is an important step. Do not skip it!

Create the Certificate - The whole nine-yard

That's quite clear. Unfortunately it is now fallen into those "suddenly outdated" developers' tutorials and bookmarks, because "Modern browsers now throw a security error". So the following are the summary of the above stackoverflow question, to the best of my knowledge.

To create a self-signed certificate, which does not chain back to a trusted anchor, involves:

  1. Create your own authority (i.e., become a CA)
  2. Create a certificate signing request (CSR) for the server
  3. Sign the server's CSR with your CA key

But the devil is in the details:

  • The above may take several steps, for each of them, but it may also be done in one command.
  • Some command in the out-dated articles uses SHA-1 hash algorithm which in many browsers throws warnings in console, because now "SHA-1 certificates" are considered not secure. So remember to use -sha256 to generate SHA-256-based certificates.
  • However, there is a saying that, using SHA-2 does not add any security to a self-signed certificate (updated May 2018). If it concerns you, check the details here.
  • "You will be prompted to add identifying information", so use -subj '/CN=localhost' (replace localhost with your desired domain) to suppress the whole list of questions about the contents of the certificate. However, be aware that using CN is now deprecated, because of the following point.
  • Modern browsers now throw a security error for otherwise well-formed self-signed certificates if they are missing a SAN (Subject Alternate Name), so many developers' tutorials and bookmarks are suddenly outdated. There has been a while that OpenSSL had not been able to provide a command-line way to specify SAN, and you had to create a short, stand-alone conf file first. Many examples are doing that way, however,
  • As of 2019, all can be done in one single command, including SAN.
  • Note that to complicate things up, browsers follow the CA/Browser Forum policies; and not the IETF policies. That's one of the reasons a certificate created with OpenSSL (which generally follows the IETF) sometimes does not validate under a browser (browsers follow the CA/B). They are different standards, they have different issuing policies and different validation requirements.
  • Thus, it's important to put DNS name in the SAN and not the CN, because both the IETF and the CA/Browser Forums specify the practice. They also specify that DNS names in the CN are deprecated (but not prohibited). If you put a DNS name in the CN, then it must be included in the SAN under the CA/B policies. So you can't avoid using the Subject Alternate Name SAN.
  • The self-signed certificate provides the same level of encryption as a certificate signed by a trusted authority (costing around $1000 ~ $2000 per year), however it is not validated with any third party unless you import it to the browsers, once.

Create the Certificate - The actual command

As of 2019, the following command serves all your needs, including SAN:

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout example.key -out example.crt -extensions san -config <(echo "[req]"; echo distinguished_name=req; echo "[san]"; echo subjectAltName=DNS:example.com,DNS:example.net,IP:10.0.0.1) -subj /CN=example.com
Enter fullscreen mode Exit fullscreen mode

Further, with OpenSSL ≥ 1.1.1, this can be shortened to:

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout example.key -out example.crt -subj /CN=example.com -addext subjectAltName=DNS:example.com,DNS:example.net,IP:10.0.0.1
Enter fullscreen mode Exit fullscreen mode

It creates a certificate that is

  • valid for the domains example.com and example.net (SAN),
  • also valid for the IP address 10.0.0.1 (SAN),
  • relatively strong (as of 2019) and
  • valid for 3650 days (~10 years).

It creates the following files:

  • Private key: example.key
  • Certificate: example.crt

All information is provided at the command line. There is no annoying interactive input. There is no messing around with config files. All necessary steps are executed by this single OpenSSL invocation: from private key generation up to the self-signed certificate.

Remark #1: Crypto parameters

Since the certificate is self-signed and needs to be accepted by users manually, it doesn't make sense to use a short expiration or weak cryptography.

In the future, you might want to use more than 4096 bits for the RSA key and a hash algorithm stronger than sha256, but as of 2019 these are sane values. They are sufficiently strong while being supported by all modern browsers.

Remark #2: Parameter "-nodes"

Theoretically you could leave out the -nodes parameter (which means "no DES encryption"), in which case example.key would be encrypted with a password. However, this is almost never useful for a server installation, because you would either have to store the password on the server as well, or you'd have to enter it manually on each reboot.

Remark #3: MinGW

On Windows in the MinGW bash, you should prefix the command with MSYS_NO_PATHCONV=1:

MSYS_NO_PATHCONV=1 openssl ...
Enter fullscreen mode Exit fullscreen mode

Alternatively, run the command in the plain cmd.exe Windows command prompt.

Remark #4: See also

To combine the two (Private key .key & Certificate .crt) into a .pem file:

cat server.crt server.key > cert.pem

Moreover, in order to use the self-signed certificate, you have to

  • Install the server certificate on the server
  • Install the CA certificate on the client

which we've already covered in the previous articles.

Create the Certificate - Parameter explained

Here are the options described in more detail, from the documentation:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days XXX
Enter fullscreen mode Exit fullscreen mode
req

PKCS#10 certificate request and certificate generating utility.

-x509

this option outputs a self signed certificate instead of a certificate request.
This is typically used to generate a test certificate or a self signed root CA.

-newkey arg

this option creates a new certificate request and a new private key. The argument
takes one of several forms. rsa:nbits, where nbits is the number of bits,
generates an RSA key nbits in size.

-keyout filename

this gives the filename to write the newly created private key to.

-out filename

This specifies the output filename to write to or standard output by default.

-days n

when the -x509 option is being used this specifies the number of days to certify
the certificate for. The default is 30 days.

-nodes

if this option is specified then if a private key is created it will not be encrypted.

The documentation is actually more detailed than the above summary.

Create the Certificate - For the public

Should you want to get a real certificate that will be recognizable by anyone on the public Internet then the procedure is below.

  1. Generate a private key
  2. Use that private key to create a CSR file
  3. Submit CSR to CA (Verisign or others, etc.)
  4. Install received cert from CA on web server
  5. Add other certs to authentication chain depending on the type cert

check the details here.

Top comments (5)

Collapse
 
eaglerob profile image
eaglerob

Or you can just use mkcert github.com/FiloSottile/mkcert
I use it like this on windows
mkcert.exe "*.dev.local" localhost 127.0.0.1

Collapse
 
suntong profile image
suntong

Cool! Thanks a lot for that!!!

Collapse
 
praneetnadkar profile image
Praneet Nadkar

Good one! I am now wondering, can makecert.exe do all of this?

Collapse
 
suntong profile image
suntong • Edited

"makecert.exe"? Oh, I didn't know it. I assume it is windows based, right?

Collapse
 
praneetnadkar profile image
Praneet Nadkar

yes. A part of windows SDK.