How to Create a Self-Signed SSL Certificate with OpenSSL

By 

Updated on

11 min read

Self-Signed SSL Certificate

A self-signed SSL certificate is an identity certificate signed by its own creator rather than a trusted certificate authority (CA). Self-signed certificates provide the same level of encryption as CA-signed certificates, but browsers will display a security warning because the certificate chain cannot be verified.

Self-signed certificates are commonly used for development, testing, and internal services. For production systems exposed to the Internet, use a certificate from a trusted CA such as Let’s Encrypt .

This guide explains how to create a self-signed SSL certificate on Linux using the openssl command-line tool.

For a broader command reference covering keys, CSRs, certificate inspection, format conversion, and live TLS testing, see our OpenSSL guide .

Prerequisites

The OpenSSL toolkit is required to generate a self-signed certificate.

To check whether the openssl package is installed on your Linux system, open your terminal, type openssl version, and press Enter. If the package is installed, the system will print the OpenSSL version, otherwise you will see something like openssl command not found.

If the openssl package is not installed on your system, you can install it with your distribution’s package manager:

  • Ubuntu, Debian, and Derivatives

    Terminal
    sudo apt install openssl
  • Fedora, RHEL, and Derivatives

    Terminal
    sudo dnf install openssl

Creating a Self-Signed SSL Certificate

To create a new self-signed SSL certificate, use the openssl req command:

sh
openssl req -newkey rsa:4096 \
            -x509 \
            -sha256 \
            -days 3650 \
            -noenc \
            -out example.crt \
            -keyout example.key

Here is what each option means:

  • -newkey rsa:4096 - Creates a new certificate request and 4096 bit RSA key. The default is 2048 bits.
  • -x509 - Creates a X.509 certificate.
  • -sha256 - Use 256-bit SHA (Secure Hash Algorithm).
  • -days 3650 - The number of days to certify the certificate for. 3650 is ten years. You can use any positive integer.
  • -noenc - Creates a key without a passphrase. Older guides use -nodes, which OpenSSL 3.x still accepts as an alias.
  • -out example.crt - Specifies the filename to write the newly created certificate to. You can specify any file name.
  • -keyout example.key - Specifies the filename to write the newly created private key to. You can specify any file name.

For more information about the openssl req command options, visit the OpenSSL req documentation page .

Once you run the command, OpenSSL generates the private key, printing progress dots while it works, and then asks you a series of questions. The information you provide is used to generate the certificate.

output
...+..+.......+...+++++++++++++++++++++++++++++++++++++++++++++*......+....+++++
.....+....+...+..+++++++++++++++++++++++++++++++++++++++++++++*.....+.......+++++
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----

Enter the information requested and press Enter:

output
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Alabama
Locality Name (eg, city) []:Montgomery
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Linuxize
Organizational Unit Name (eg, section) []:Marketing
Common Name (e.g. server FQDN or YOUR name) []:linuxize.com
Email Address []:hello@linuxize.com

The certificate and private key will be created at the specified location. Use the ls command to verify that the files were created:

Terminal
ls
output
example.crt example.key

That is it. You have generated a new self-signed SSL certificate.

It is always a good idea to back up your new certificate and key to external storage.

Creating a Self-Signed SSL Certificate without Prompt

If you want to generate a self-signed SSL certificate without being prompted for any question, use the -subj option and specify all the subject information:

sh
openssl req -newkey rsa:4096 \
            -x509 \
            -sha256 \
            -days 3650 \
            -noenc \
            -out example.crt \
            -keyout example.key \
            -subj "/C=SI/ST=Ljubljana/L=Ljubljana/O=Security/OU=IT Department/CN=www.example.com"
output
...+.....+.+..+....+...+++++++++++++++++++++++++++++++++++++++++++++*...+++++
..+....+..+.+..+.......+++++++++++++++++++++++++++++++++++++++++++++*.....+++++
-----

OpenSSL still prints the key generation progress, but skips every prompt because the subject came from the command line.

The fields specified in the -subj line are listed below:

  • C= - Country name. The two-letter ISO abbreviation.
  • ST= - State or Province name.
  • L= - Locality Name. The name of the city where you are located.
  • O= - The full name of your organization.
  • OU= - Organizational Unit.
  • CN= - The fully qualified domain name.

Creating a Certificate with Subject Alternative Name (SAN)

Modern browsers and applications require the Subject Alternative Name (SAN) extension for hostname validation. Certificates that rely only on the Common Name (CN) field will trigger warnings in most clients.

To include SAN entries, pass an -addext option:

sh
openssl req -newkey rsa:4096 \
            -x509 \
            -sha256 \
            -days 3650 \
            -noenc \
            -out example.crt \
            -keyout example.key \
            -subj "/C=US/ST=Alabama/L=Montgomery/O=MyOrg/CN=example.com" \
            -addext "subjectAltName=DNS:example.com,DNS:www.example.com,IP:10.0.0.1"

The -addext option is available in OpenSSL 1.1.1 and later. You can specify multiple DNS names and IP addresses separated by commas.

Generating an ECDSA Certificate

If you prefer a smaller key size and faster TLS handshakes, you can generate an ECDSA certificate instead of RSA:

sh
openssl req -new \
            -x509 \
            -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
            -sha256 \
            -days 3650 \
            -noenc \
            -out example.crt \
            -keyout example.key \
            -subj "/C=US/ST=Alabama/L=Montgomery/O=MyOrg/CN=example.com" \
            -addext "subjectAltName=DNS:example.com,DNS:www.example.com"

The prime256v1 curve (also known as P-256) is widely supported and provides security equivalent to a 3072-bit RSA key.

Verifying the Certificate

To view the details of your generated certificate, use the openssl x509 command:

Terminal
openssl x509 -in example.crt -text -noout

The output displays the issuer, subject, validity period, public key type, and any extensions including the Subject Alternative Name.

To verify that the certificate and private key belong together, compare the public key that each one carries:

Terminal
openssl x509 -in example.crt -noout -pubkey | openssl sha256
openssl pkey -in example.key -pubout | openssl sha256

If both commands print the same hash, the certificate and key are a matching pair. This works for RSA and ECDSA keys alike, unlike the older -modulus comparison, which only applies to RSA.

Trusting the Certificate on Linux

A self-signed certificate is not signed by a recognized authority, so curl, wget, and git reject it by default. On a development or internal machine, add the certificate to the system trust store and those tools accept it.

On Ubuntu, Debian, and Derivatives, copy the certificate into the local anchor directory and refresh the store. The file must be PEM-encoded and use a .crt extension:

Terminal
sudo cp example.crt /usr/local/share/ca-certificates/example.crt
sudo update-ca-certificates

The command rebuilds the bundle in /etc/ssl/certs and reports how many certificates it added.

On Fedora, RHEL, and Derivatives, the anchor directory and the refresh command are different:

Terminal
sudo cp example.crt /etc/pki/ca-trust/source/anchors/example.crt
sudo update-ca-trust

Confirm that the certificate now validates against the system store:

Terminal
openssl verify example.crt
output
example.crt: OK

Browsers keep their own trust stores and ignore the system one. Firefox always uses its internal store, and Chrome and Chromium on Linux read the NSS database in your home directory. To add the certificate for Chrome, install certutil (the libnss3-tools package on Debian and Ubuntu, nss-tools on Fedora) and run:

Terminal
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "Example Self-Signed" -i example.crt

Firefox imports certificates under Settings, Privacy & Security, View Certificates, Authorities, Import.

Using the Certificate with Nginx and Apache

Move the certificate and key into the standard locations, then restrict the key so only root can read it:

Terminal
sudo cp example.crt /etc/ssl/certs/
sudo cp example.key /etc/ssl/private/
sudo chmod 600 /etc/ssl/private/example.key

In Nginx, point the server block at both files:

nginx
server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/ssl/certs/example.crt;
    ssl_certificate_key /etc/ssl/private/example.key;

    root /var/www/example.com;
}

Test the configuration and reload the service:

Terminal
sudo nginx -t
sudo systemctl reload nginx

In Apache, enable the SSL module first, which on Debian and Ubuntu means running sudo a2enmod ssl, then set the paths in the virtual host:

apache
<VirtualHost *:443>
    ServerName example.com
    DocumentRoot /var/www/example.com

    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/example.crt
    SSLCertificateKeyFile /etc/ssl/private/example.key
</VirtualHost>

Check the syntax and reload Apache:

Terminal
sudo apachectl configtest
sudo systemctl reload apache2

Visitors still see a browser warning unless the certificate is in their trust store. This is why self-signed certificates suit internal services rather than public sites.

Creating Your Own CA and Signing Certificates

Trusting one self-signed certificate per service becomes tedious once you run several of them. Create your own certificate authority instead, trust that CA once, and sign as many server certificates as you need.

Start with the CA key and a self-signed CA certificate:

sh
openssl req -x509 -newkey rsa:4096 \
            -sha256 \
            -days 3650 \
            -noenc \
            -keyout ca.key -out ca.crt \
            -subj "/C=US/ST=Alabama/L=Montgomery/O=MyOrg/CN=MyOrg Root CA"

Next, create a key and a certificate signing request for the server, including the SAN entries the certificate should cover:

sh
openssl req -newkey rsa:2048 \
            -noenc \
            -keyout server.key -out server.csr \
            -subj "/C=US/ST=Alabama/L=Montgomery/O=MyOrg/CN=example.com" \
            -addext "subjectAltName=DNS:example.com,DNS:www.example.com"

Sign the request with the CA. The -copy_extensions copy option carries the SAN entries from the request into the issued certificate, and -CAcreateserial generates the serial number file on first use:

sh
openssl x509 -req -in server.csr \
             -CA ca.crt -CAkey ca.key -CAcreateserial \
             -copy_extensions copy \
             -days 825 \
             -sha256 \
             -out server.crt
output
Certificate request self-signature ok
subject=C=US, ST=Alabama, L=Montgomery, O=MyOrg, CN=example.com

Confirm that the CA signed the certificate:

Terminal
openssl verify -CAfile ca.crt server.crt
output
server.crt: OK

Now add ca.crt to the trust store on each client machine, following the steps in the previous section. Every certificate you sign with this CA is trusted from that point on.

Warning
Guard ca.key as carefully as any other private key. Anyone holding it can issue certificates that your clients trust. Keep its permissions at 600, store it off the web server, and never commit it to version control.

Converting the Certificate to PKCS#12

Java keystores, Windows services, and several load balancers expect the certificate and key bundled into a single PKCS#12 file rather than two PEM files. Combine them with openssl pkcs12:

Terminal
openssl pkcs12 -export -out example.pfx -inkey example.key -in example.crt

OpenSSL prompts for an export password that protects the bundle. Inspect the result to confirm it holds both parts:

Terminal
openssl pkcs12 -in example.pfx -info -noout

For conversions between other encodings, such as PEM to DER, see our OpenSSL guide .

Quick Reference

For a printable quick reference, see the OpenSSL cheatsheet .

TaskCommand
Interactive self-signed certificateopenssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -noenc -out example.crt -keyout example.key
Skip the promptsAdd -subj "/C=US/ST=State/L=City/O=Org/CN=example.com"
Add SAN entriesAdd -addext "subjectAltName=DNS:example.com,IP:10.0.0.1"
Use ECDSA instead of RSA-newkey ec -pkeyopt ec_paramgen_curve:prime256v1
Inspect the certificateopenssl x509 -in example.crt -text -noout
Check expiry datesopenssl x509 -in example.crt -noout -dates
Match key to certificateopenssl x509 -in example.crt -noout -pubkey | openssl sha256
Trust on Ubuntu, DebianCopy to /usr/local/share/ca-certificates/, run update-ca-certificates
Trust on Fedora, RHELCopy to /etc/pki/ca-trust/source/anchors/, run update-ca-trust
Export to PKCS#12openssl pkcs12 -export -out example.pfx -inkey example.key -in example.crt

Troubleshooting

curl: (60) SSL certificate problem
The certificate is not in the system trust store, which is expected for a self-signed certificate. Add it to the trust store as shown above, or pass --cacert example.crt for a single request. Use -k only for throwaway testing, since it disables verification completely.

NET::ERR_CERT_AUTHORITY_INVALID in the browser
The browser cannot build a chain to a trusted root. Import the certificate into the browser trust store, or create your own CA and trust that instead.

NET::ERR_CERT_COMMON_NAME_INVALID
The certificate carries no Subject Alternative Name covering the hostname you requested. Modern browsers ignore the Common Name field. Regenerate the certificate with -addext "subjectAltName=DNS:yourhost".

Certificate and key hashes do not match
The two files came from separate runs. Generate both together, or locate the key that pairs with the certificate. Nginx and Apache refuse to start on a mismatch.

The certificate has expired
Self-signed certificates are not renewed, they are replaced. Run the same openssl req command again with a fresh -days value, copy both files back to the server, and reload the service.

FAQ

Is a self-signed certificate secure?
The encryption is identical to a CA-signed certificate. What is missing is identity verification, so a client has no way to confirm the server is who it claims to be. That makes self-signed certificates fine for internal and test systems, and unsuitable for public sites.

How long should a self-signed certificate be valid?
You control both ends, so long validity periods such as ten years are common for internal use. If a browser validates the certificate, keep it under 825 days, since some clients reject longer-lived certificates.

Can I create a wildcard self-signed certificate?
Yes. Pass the wildcard as a SAN entry, for example -addext "subjectAltName=DNS:*.example.com,DNS:example.com". Include the bare domain as well, since a wildcard does not cover it.

What is the difference between the .crt and .key files?
The .crt file holds the public certificate and is safe to distribute. The .key file holds the private key and must stay secret, readable only by the service that uses it.

Conclusion

You now have a self-signed certificate, a way to make Linux tools trust it, and a server configuration that serves it. When several internal services need certificates, create your own CA once and sign from it rather than trusting each certificate separately. For a public site, a certificate from Let’s Encrypt removes the browser warning entirely.

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page