Generate a Self-Signed Certificate with make-ssl-cert

A new service needs TLS today, and make-ssl-cert can hand you a working self-signed PEM pair in under a minute. This guide covers generating the distribution snakeoil certificate, making a separate test certificate, checking what came out, and knowing when a self-signed cert is genuinely the right tool. Examples use ssl-cert 1.1.2ubuntu1 and OpenSSL 3.6.1. Allow about fifteen minutes.

This is fine for local testing, development, and any service that explicitly expects snakeoil. It is not a route to a publicly trusted certificate: any browser or client that does not already trust your CA will warn about the result.

1. Check the installed command

Start with read-only checks. Nothing here needs elevated privileges:

$ command -v make-ssl-cert
/usr/sbin/make-ssl-cert
$ dpkg-query -W -f='${Package} ${Version}\n' ssl-cert
ssl-cert 1.1.2ubuntu1
$ make-ssl-cert --help
Usage: make-ssl-cert [options] <template> <output>
       make-ssl-cert [options] generate-default-snakeoil

Checkpoint: confirm the template exists before trying manual mode:

$ test -r /usr/share/ssl-cert/ssleay.cnf && echo 'template is readable'
template is readable

2. Generate the distribution snakeoil certificate

On a normal Debian or Ubuntu box, this is the direct route:

$ sudo make-ssl-cert generate-default-snakeoil

This is an elevated, state-changing command. It can create or replace /etc/ssl/certs/ssl-cert-snakeoil.pem and /etc/ssl/private/ssl-cert-snakeoil.key, and it adds a hash link beside the certificate. Do not run it on a production host purely to quiet a client warning.

It normally refuses to replace a usable existing certificate: if both standard files exist, it checks the signature and RSA key length first to decide whether regeneration is actually needed. --no-overwrite makes an existing pair a hard stop; --force-overwrite forces a new pair regardless.

Warning: --force-overwrite invalidates the old private key for everything already using it. Stop the dependent service first if its reload behaviour is unclear, and keep a tested config backup. Generation itself does not configure or reload a web server.

Checkpoint: inspect the result without printing the private key:

$ sudo stat -c '%A %U:%G %n' \
    /etc/ssl/certs/ssl-cert-snakeoil.pem \
    /etc/ssl/private/ssl-cert-snakeoil.key
-rw-r--r-- root:root /etc/ssl/certs/ssl-cert-snakeoil.pem
-rw-r----- root:ssl-cert /etc/ssl/private/ssl-cert-snakeoil.key
$ sudo openssl x509 -in /etc/ssl/certs/ssl-cert-snakeoil.pem \
    -noout -subject -issuer -dates -ext subjectAltName
subject=CN = your-hostname
issuer=CN = your-hostname
notBefore=...
notAfter=...
X509v3 Subject Alternative Name:
    DNS:your-hostname

Subject and dates are host-specific. What matters is that the private key is not world-readable; the exact owner can vary with packaging, but the restrictive mode on the key is the real boundary.

3. Create a separate test certificate

Manual mode suits an application that needs a certificate at a temporary or app-specific path. It asks debconf for a hostname and optional alternative name, then writes certificate and key together to your chosen output:

$ install -d -m 700 ./tls-test
$ make-ssl-cert /usr/share/ssl-cert/ssleay.cnf ./tls-test/test.pem

The output path must not already exist, unless you deliberately pass --force-overwrite. The wrapper sets a restrictive umask and applies mode 600 to a manually generated output. Treat test.pem as a private-key file even though it also holds the certificate.

For a non-interactive run, make sure debconf already has the intended hostname and alternative name set. Do not assume the machine's own hostname is what clients actually use: a wrong name is a certificate-identity problem, not something a client retry will fix.

Checkpoint: inspect only the public certificate through OpenSSL:

$ openssl x509 -in ./tls-test/test.pem -noout \
    -subject -issuer -dates -ext subjectAltName
subject=CN = your-test-name
issuer=CN = your-test-name
notBefore=...
notAfter=...
X509v3 Subject Alternative Name:
    DNS:your-test-name

If you need the hash link the wrapper normally creates, look in the output directory for a symlink whose name starts with the certificate's OpenSSL subject hash:

$ openssl x509 -hash -noout -in ./tls-test/test.pem
...hash...
$ find ./tls-test -maxdepth 1 -type l -printf '%f -> %l\n'

4. Do not trust --expiration-days blindly

The manual page documents --expiration-days N with a default of 3650 days. On the installed 1.1.2ubuntu1 wrapper, though, a local test with --expiration-days 30 produced a certificate that expired three days later, consistent with the wrapper passing the option parser's position instead of the requested value.

Warning: never put an unverified lifetime into an automated deployment. Generate into a disposable path and check the resulting dates first:

$ umask 077
$ make-ssl-cert --expiration-days 30 \
    /usr/share/ssl-cert/ssleay.cnf ./tls-test/expiry-test.pem
$ openssl x509 -in ./tls-test/expiry-test.pem -noout -dates
notBefore=...
notAfter=...

Compare notAfter with the lifetime you requested before installing the file. If it is wrong, do not use that certificate. Remove only the disposable test file with rm -- ./tls-test/expiry-test.pem, an irreversible step, then report or patch the package through your normal maintenance process.

5. Handle common failures

Avoid copying a certificate generated for one hostname onto another service: it can stay syntactically valid while still failing hostname verification.

Undo the manual example

Stop anything using the test file, then remove the disposable directory:

$ rm -rf -- ./tls-test

Recovery: never run that removal against /etc/ssl. For the default snakeoil pair, restore the previous files from backup and reload the consuming service per its own documentation. Regenerating the pair is not an undo, it creates a new identity.

Done means