Categories
certificate openssl ssl ssl-certificate x509certificate

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

1764

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

openssl req -new > cert.csr
openssl rsa -in privkey.pem -out key.pem
openssl x509 -in cert.csr -out cert.pem -req -signkey key.pem -days 1001
cat key.pem>>cert.pem

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?

6

  • 57

    Self-signed certificates are considered insecure for the Internet. Firefox will treat the site as having an invalid certificate, while Chrome will act as if the connection was plain HTTP. More details: gerv.net/security/self-signed-certs

    Apr 16, 2012 at 14:17

  • 54

    You need to import your CA certificate into your browsers and tell the browsers you trust the certificate -or- get it signed by one of the big money-for-nothing organizations that are already trusted by the browsers -or- ignore the warning and click past it. I like the last option myself.

    – trojanfoe

    Apr 16, 2012 at 14:20

  • 20

    You should not use the “stock” OpenSSL settings like that. That’s because you cannot place DNS names in the Subject Alternate Name (SAN). You need to provide a configuration file with an alternate_names section and pass it with the -config option. Also, placing a DNS name in the Common Name (CN) is deprecated (but not prohibited) by both the IETF and the CA/Browser Forums. Any DNS name in the CN must also be present in the SAN. There’s no way to avoid using the SAN. See answer below.

    – jww

    Jan 13, 2015 at 22:01


  • 6

    In addition to @jww ‘s comment. Per may 2017 Chrome doesn’t accept certs w/o (emtpy) SAN’s anymore: “The certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.”

    – GerardJP

    May 25, 2017 at 7:35


  • 9

    These days, as long as your webserver is accessible by its FQDN on port 80 over the internet, you can use LetsEncrypt and get free full CA certs (valid for 90 days, renewal can be automated) that won’t give any browser warnings/messages. www.letsencrypt.com

    Mar 27, 2018 at 12:13


2837

You can do that in one command:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365

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).

31

  • 23

    For anyone who’s interested, here is the documentation, if you want to verify anything yourself.

    – user456814

    Apr 15, 2014 at 17:34

  • 26

    How does signing with a 3rd-party provide more security?

    Jul 11, 2014 at 3:14

  • 303

    For anyone else using this in automation, here’s all of the common parameters for the subject: -subj "/C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=www.example.com"

    – Alex S

    Jun 5, 2015 at 18:13


  • 61

    @JamesMills I mean, think about it — if a shady looking guy with “free candy” written on the side of his van invites you to come inside, you’re totally going to think twice and be on guard about it — but if someone you trust — like really trust — is all like, “naw man, he’s legit” you’re going to be all about that free candy.

    Dec 18, 2015 at 23:39


  • 86

    Remember to use -sha256 to generate SHA-256-based certificate.

    Jan 25, 2016 at 6:13

636

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):

[ alternate_names ]

DNS.1       = example.com
DNS.2       = www.example.com
DNS.3       = mail.example.com
DNS.4       = ftp.example.com

# Add these if you need them. But usually you don't want them or
#   need them in production. You may need them for development.
# DNS.5       = localhost
# DNS.6       = localhost.localdomain
# IP.1        = 127.0.0.1
# IP.2        = ::1

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):

openssl req -config example-com.conf -new -x509 -sha256 -newkey rsa:2048 -nodes \
    -keyout example-com.key.pem -days 365 -out example-com.cert.pem

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

openssl req -config example-com.conf -new -sha256 -newkey rsa:2048 -nodes \
    -keyout example-com.key.pem -days 365 -out example-com.req.pem

Print a self-signed certificate:

openssl x509 -in example-com.cert.pem -text -noout

Print a signing request:

openssl req -in example-com.req.pem -text -noout

Configuration file (passed via -config option)

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

# The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description).
#   Its sort of a mashup. For example, RFC 4514 does not provide emailAddress.
[ 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

# Use a friendly name here because it's presented to the user. The server's DNS
#   names are placed in Subject Alternate Names. Plus, DNS names here is deprecated
#   by both IETF and CA/Browser Forums. If you place a DNS name here, then you
#   must include the DNS name in the SAN too (otherwise, Chrome and others that
#   strictly follow the CA/Browser Baseline Requirements will fail).
commonName          = Common Name (e.g. server FQDN or YOUR name)
commonName_default      = Example Company

emailAddress            = Email Address
emailAddress_default        = [email protected]

# Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
[ x509_ext ]

subjectKeyIdentifier        = hash
authorityKeyIdentifier    = keyid,issuer

# You only need digitalSignature below. *If* you don't allow
#   RSA Key transport (i.e., you use ephemeral cipher suites), then
#   omit keyEncipherment because that's key transport.
basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
subjectAltName          = @alternate_names
nsComment           = "OpenSSL Generated Certificate"

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.
# extendedKeyUsage    = serverAuth, clientAuth

# Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
[ req_ext ]

subjectKeyIdentifier        = hash

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

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.
# extendedKeyUsage    = serverAuth, clientAuth

[ alternate_names ]

DNS.1       = example.com
DNS.2       = www.example.com
DNS.3       = mail.example.com
DNS.4       = ftp.example.com

# Add these if you need them. But usually you don't want them or
#   need them in production. You may need them for development.
# DNS.5       = localhost
# DNS.6       = localhost.localdomain
# DNS.7       = 127.0.0.1

# IPv6 localhost
# DNS.8     = ::1

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.

# IPv4 localhost
# IP.1       = 127.0.0.1

# IPv6 localhost
# IP.2     = ::1

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

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.

9

  • 5

    Is it possible to use wildcards in the alternate_names section? Particularly sub-sub domains. I have a question referencing this answer here: serverfault.com/questions/711596/…

    Aug 12, 2015 at 10:02

  • 3

    I’ve just replied to his specific question. I think doesn’t make sense to add this long security description when the answer was so simple

    Feb 21, 2016 at 1:44

  • 19

    @diegows – your answer is not complete or correct. The reason it is not correct is discussed in the long post you don’t want to read 🙂

    – jww

    Feb 21, 2016 at 4:42

  • 1

    Thanks! I found your post very helpful. FYI I was recently playing with vault and found it insisted on IP.x 127.0.0.1 rather than DNS.x 127… I didn’t check if this is in the standard or not.

    – Chomeh

    Aug 22, 2016 at 0:32


  • 4

    Thank you @jww. You said, “1. Create your own authority (i.e, become a CA)”, then said, “5. Install the CA certificate on the client”. If the root key became compromised, a malicious person could sign a cert for any domain with that key, and if they trick you into going to their website, they can now do a man-in-the-middle attack. Is there a way to create the root CA such that it can only sign intermediary CAs and not certificates? Then you can protect your intermediary CA with a name constraint.

    Mar 30, 2017 at 18:41

593

As of 2022 with OpenSSL ≥ 1.1.1, the following command serves all your needs, including Subject Alternate Name (SAN):

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:www.example.net,IP:10.0.0.1"

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:

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:www.example.net,IP:10.0.0.1
    ) \
  -subj "/CN=example.com"

Either command creates a certificate that is

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

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

27

  • 4

    I tried to use the oneliner #2 (modern) on windows in mingw64, and I faced a bug with -subj parameter. ` $ openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout localhost.key -out localhost.crt -subj ‘/CN=localhost’ -addext subjectAltName=DNS:localhost,IP:127.0.0.1 Generating a RSA private key […] writing new private key to ‘localhost.key’ —– name is expected to be in the format /type0=value0/type1=value1/type2=… where characters may be escaped by \. This name is not in that format: ‘C:/Program Files/Git/CN=localhost’ problems making Certificate Request `

    Dec 23, 2018 at 14:12


  • 3

    I couldn’t figure out what exactly was to blame in the arg /CN=localhost expanding to C:/Program Files/Git/CN=localhost , so I just ran the whole command in plain cmd.exe and it worked just fine. Just in case someone is struggling with this one.

    Dec 23, 2018 at 14:15

  • 2

    @FranklinYu Are you sure that rsa:2048 will be enough in 10 years from now? Because that’s the validity period. As explained, it doesn’t make sense to use short expiration or weak crypto. Most 2048-bit RSA keys have a validity period of 1-3 years at most. Regarding OpenSSL 1.1.1, I’m still leaving sha256 in there, so it’s more explicit and obvious to change if you want a stronger hash.

    – vog

    Jan 12, 2019 at 20:18


  • 3

    If you’re using git bash on windows, like @YuriyPozniak, you will get the error he listed where /CN=localhost is being expanded to C:/Progra Files/Git/CN=localhost. If you add an extra /, then the expansion won’t occur. //CN=localhost

    Jun 25, 2019 at 5:56


  • 28

    1000 +1s for creating a “one-liner” that uses the new required SAN without having to create a long-winded config file with a lot of boilerplate. Well done!

    Sep 23, 2019 at 3:35