How to generate a self-signed SSL certificate using OpenSSL?


I’m adding HTTPS support to an embedded Linux device. I have tried to generate a self-signed certificate with these steps:

This works, but I get some errors with, for example, Google Chrome:

This is probably not the site you are looking for!
The site’s security certificate is not trusted!

Am I missing something? Is this the correct way to build a self-signed certificate?

17 Answers 17

You can do that in one command:

You can also add -nodes (short for no DES ) if you don’t want to protect your private key with a passphrase. Otherwise it will prompt you for at least a 4 character password.

The days parameter (365) you can replace with any number to affect the expiration date. It will then prompt you for things like Country Name, but you can just hit Enter and accept the defaults.

Add -subj ‘/CN=localhost’ to suppress questions about the contents of the certificate (replace localhost with your desired domain).

Self-signed certificates are not validated with any third party unless you import them to the browsers previously. If you need more security, you should use a certificate signed by a certificate authority (CA).

Am I missing something? Is this the correct way to build a self-signed certificate?

It’s easy to create a self-signed certificate. You just use the openssl req command. It can be tricky to create one that can be consumed by the largest selection of clients, like browsers and command line tools.

It’s difficult because the browsers have their own set of requirements, and they are more restrictive than the IETF. The requirements used by browsers are documented at the CA/Browser Forums (see references below). The restrictions arise in two key areas: (1) trust anchors, and (2) DNS names.

Modern browsers (like the warez we’re using in 2014/2015) want a certificate that chains back to a trust anchor, and they want DNS names to be presented in particular ways in the certificate. And browsers are actively moving against self-signed server certificates.

Some browsers don’t exactly make it easy to import a self-signed server certificate. In fact, you can’t with some browsers, like Android’s browser. So the complete solution is to become your own authority.

In the absence of becoming your own authority, you have to get the DNS names right to give the certificate the greatest chance of success. But I would encourage you to become your own authority. It’s easy to become your own authority, and it will sidestep all the trust issues (who better to trust than yourself?).

This is probably not the site you are looking for!
The site’s security certificate is not trusted!

This is because browsers use a predefined list of trust anchors to validate server certificates. A self-signed certificate does not chain back to a trusted anchor.

The best way to avoid this is:

  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
  4. Install the server certificate on the server
  5. Install the CA certificate on the client

Step 1 — Create your own authority just means to create a self-signed certificate with CA: true and proper key usage. That means the Subject and Issuer are the same entity, CA is set to true in Basic Constraints (it should also be marked as critical), key usage is keyCertSign and crlSign (if you are using CRLs), and the Subject Key Identifier (SKI) is the same as the Authority Key Identifier (AKI).

To become your own certificate authority, see *How do you sign a certificate signing request with your certification authority? on Stack Overflow. Then, import your CA into the Trust Store used by the browser.

Steps 2 — 4 are roughly what you do now for a public facing server when you enlist the services of a CA like Startcom or CAcert. Steps 1 and 5 allows you to avoid the third-party authority, and act as your own authority (who better to trust than yourself?).

The next best way to avoid the browser warning is to trust the server’s certificate. But some browsers, like Android’s default browser, do not let you do it. So it will never work on the platform.

The issue of browsers (and other similar user agents) not trusting self-signed certificates is going to be a big problem in the Internet of Things (IoT). For example, what is going to happen when you connect to your thermostat or refrigerator to program it? The answer is, nothing good as far as the user experience is concerned.

The W3C’s WebAppSec Working Group is starting to look at the issue. See, for example, Proposal: Marking HTTP As Non-Secure.

How to create a self-signed certificate with OpenSSL

The commands below and the configuration file create a self-signed certificate (it also shows you how to create a signing request). They differ from other answers in one respect: the DNS names used for the self signed certificate are in the Subject Alternate Name (SAN), and not the Common Name (CN).

The DNS names are placed in the SAN through the configuration file with the line subjectAltName = @alternate_names (there’s no way to do it through the command line). Then there’s an alternate_names section in the configuration file (you should tune this to suit your taste):

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.

If you don’t do put DNS names in the SAN, then the certificate will fail to validate under a browser and other user agents which follow the CA/Browser Forum guidelines.

Related: 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.

Create a self signed certificate (notice the addition of -x509 option):

Create a signing request (notice the lack of -x509 option):

Print a self-signed certificate:

Print a signing request:

Configuration file (passed via -config option)

You may need to do the following for Chrome. Otherwise Chrome may complain a Common Name is invalid ( ERR_CERT_COMMON_NAME_INVALID ). I’m not sure what the relationship is between an IP address in the SAN and a CN in this instance.

There are other rules concerning the handling of DNS names in X.509/PKIX certificates. Refer to these documents for the rules:

  • RFC 5280, Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile
  • RFC 6125, Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)
  • RFC 6797, Appendix A, HTTP Strict Transport Security (HSTS)
  • RFC 7469, Public Key Pinning Extension for HTTP
  • CA/Browser Forum Baseline Requirements
  • CA/Browser Forum Extended Validation Guidelines

RFC 6797 and RFC 7469 are listed, because they are more restrictive than the other RFCs and CA/B documents. RFCs 6797 and 7469 do not allow an IP address, either.

As of 2022 with OpenSSL ≥ 1.1.1, the following command serves all your needs, including SAN:

On old systems with OpenSSL ≤ 1.1.0, such as Debian ≤ 9 or CentOS ≤ 7, a longer version of this command needs to be used:

Either command creates a certificate that is

  • valid for the (sub)domains and (SAN),
  • also valid for the IP address (SAN),
  • relatively strong (as of 2022) and
  • valid for 3650 days (

The following files are generated:

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

All information is provided at the command line. There is no interactive input that annoys you. There are no config files you have to mess around with. All necessary steps are executed by a 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 2022 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: See also

Here are the options described in @diegows’s answer, described in more detail, from the documentation:

PKCS#10 certificate request and certificate generating utility.

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.

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.

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

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

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

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; I just summarized it here.

I can’t comment, so I will put this as a separate answer. I found a few issues with the accepted one-liner answer:

  • The one-liner includes a passphrase in the key.
  • The one-liner uses SHA-1 which in many browsers throws warnings in console.

Here is a simplified version that removes the passphrase, ups the security to suppress warnings and includes a suggestion in comments to pass in -subj to remove the full question list:

Replace ‘localhost’ with whatever domain you require. You will need to run the first two commands one by one as OpenSSL will prompt for a passphrase.

To combine the two into a .pem file:

Modern browsers now throw a security error for otherwise well-formed self-signed certificates if they are missing a SAN (Subject Alternate Name). OpenSSL does not provide a command-line way to specify this, so many developers’ tutorials and bookmarks are suddenly outdated.

The quickest way to get running again is a short, stand-alone conf file:

Create an OpenSSL config file (example: req.cnf )

Create the certificate referencing this config file

I would recommend to add the -sha256 parameter, to use the SHA-2 hash algorithm, because major browsers are considering to show "SHA-1 certificates" as not secure.

The same command line from the accepted answer — @diegows with added -sha256

openssl req -x509 -sha256 -newkey rsa:2048 -keyout key.pem -out cert.pem -days XXX

Update May 2018. As many noted in the comments that using SHA-2 does not add any security to a self-signed certificate. But I still recommend using it as a good habit of not using outdated / insecure cryptographic hash functions. Full explanation is available in Why is it fine for certificates above the end-entity certificate to be SHA-1 based?.

This is the script I use on local boxes to set the SAN (subjectAltName) in self-signed certificates.

This script takes the domain name ( and generates the SAN for * and in the same certificate. The sections below are commented. Name the script (e.g. ) and give it executable permissions. The files will be written to the same directory as the script.

Chrome 58 an onward requires SAN to be set in self-signed certificates.

This script also writes an information file, so you can inspect the new certificate and verify the SAN is set properly.

If you are using Apache, then you can reference the above certificate in your configuration file like so:

Remember to restart your Apache (or Nginx, or IIS) server for the new certificate to take effect.

I can`t comment so I add a separate answer. I tried to create a self-signed certificate for NGINX and it was easy, but when I wanted to add it to Chrome white list I had a problem. And my solution was to create a Root certificate and signed a child certificate by it.

So step by step. Create file config_ssl_ca.cnf Notice, config file has an option basicConstraints=CA:true which means that this certificate is supposed to be root.

This is a good practice, because you create it once and can reuse.

Next config file for your child certificate will be call config_ssl.cnf.

The first step — create Root key and certificate

The second step creates child key and file CSR — Certificate Signing Request. Because the idea is to sign the child certificate by root and get a correct certificate

Open Linux terminal and do this command

The text file containing the next serial number to use in hex. Mandatory. This file must be present and contain a valid serial number.

Last Step, create one more config file and call it config_ca.cnf

You may ask, why so difficult, why we must create one more config to sign child certificate by root. The answer is simple because child certificate must have a SAN block — Subject Alternative Names. If we sign the child certificate by openssl x509 utils, the Root certificate will delete the SAN field in child certificate. So we use openssl ca instead of openssl x509 to avoid the deleting of the SAN field. We create a new config file and tell it to copy all extended fields copy_extensions = copy.

The program asks you 2 questions:

  1. Sign the certificate? Say Y
  2. 1 out of 1 certificate requests certified, commit? Say Y

In terminal you can see a sentence with the word Database, it means file index.txt which you create by the command touch. It will contain all information by all certificates you create by openssl ca util. To check the certificate valid use:

If you want to see what inside in CRT:

If you want to see what inside in CSR:

This also works in Chrome 57, as it provides the SAN, without having another configuration file. It was taken from an answer here.

This creates a single .pem file that contains both the private key and cert. You can move them to separate .pem files if needed.

One-liner version 2017:



Edit: added prepending Slash to ‘subj’ option for Ubuntu.

You have the general procedure correct. The syntax for the command is below.

However, the warnings are displayed, because the browser was not able to verify the identify by validating the certificate with a known Certificate Authority (CA).

As this is a self-signed certificate there is no CA and you can safely ignore the warning and proceed. 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

Generate keys

I am using /etc/mysql for cert storage because /etc/apparmor.d/usr.sbin.mysqld contains /etc/mysql/*.pem r .

Add configuration

On my setup, Ubuntu server logged to: /var/log/mysql/error.log

Follow up notes:

SSL error: Unable to get certificate from ‘. ‘

MySQL might be denied read access to your certificate file if it is not in apparmors configuration. As mentioned in the previous steps^, save all our certificates as .pem files in the /etc/mysql/ directory which is approved by default by apparmor (or modify your apparmor/SELinux to allow access to wherever you stored them.)

SSL error: Unable to get private key

Convert generated rsa:2048 to plain rsa with:

Verifying connection

When logged in to the MySQL instance, you can issue the query:

If your connection is not encrypted, the result will be blank:

Otherwise, it would show a non-zero length string for the cypher in use:

One liner FTW. I like to keep it simple. Why not use one command that contains ALL the arguments needed? This is how I like it — this creates an x509 certificate and its PEM key:

That single command contains all the answers you would normally provide for the certificate details. This way you can set the parameters and run the command, get your output — then go for coffee.

The primary reason one does not want to get a signed certificate from a certificate authority is cost — Symantec charges between $995 — $1,999 per year for certificates — just for a certificate intended for internal network, Symantec charges $399 per year. That cost is easy to justify if you are processing credit card payments or work for the profit center of a highly profitable company. It is more than many can afford for a personal project one is creating on the internet, or for a non-profit running on a minimal budget, or if one works in a cost center of an organization — cost centers always try to do more with less.

An alternative is to use certbot (see about certbot). Certbot is an easy-to-use automatic client that fetches and deploys SSL/TLS certificates for your web server.

If you setup certbot, you can enable it to create and maintain a certificate for you issued by the Let’s Encrypt certificate authority.

I did this over the weekend for my organization. I installed the required packages for certbot on my server (Ubuntu 16.04) and then ran the command necessary to setup and enable certbot. One likely needs a DNS plugin for certbot — we are presently using DigitalOcean though may be migrating to another service soon.

Note that some of the instructions were not quite right and took a little poking and time with Google to figure out. This took a fair amount of my time the first time but now I think I could do it in minutes.

For DigitalOcean, one area I struggled was when I was prompted to input the path to your DigitalOcean credentials INI file. What the script is referring to is the Applications API page and the Tokens/Key tab on that page. You need to have or generate a personal access token (read and write) for DigitalOcean’s API — this is a 65 character hexadecimal string. This string then needs to be put into a file on the webserver from which you are running certbot. That file can have a comment as its first line (comments start with #). The seccond line is:

Once I figured out how to set up a read+write token for DigitalOcean’s API, it was pretty easy to use certbot to setup a wildcard certificate. Note that one does not have to setup a wildcard certificate, one may instead specify each domain and sub-domain that one wants the certificate to appply to. It was the wildcard certificate that required the credentials INI file that contained the personal access token from DigitalOcean.

Note that public key certificates (also known as identity certificates or SSL certificates) expire and require renewal. Thus you will need to renew your certificate on a periodic (reoccurring) basis. The certbot documentation covers renewing certificates.

My plan is to write a script to use the openssl command to get my certificate’s expiration date and to trigger renewal when it is 30 days or less until it expires. I will then add this script to cron and run it once per day.