Sign a CSR with your own CA
To sign a CSR with your own CA, run `openssl x509 -req -in request.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out certificate.crt -days 397 -sha256 -copy_extensions copyall`. The `-copy_extensions` flag is essential: without it OpenSSL silently discards every extension in the CSR, including the subject alternative names, and the resulting certificate is rejected by every browser. Safer still is to ignore the CSR's extensions entirely and supply your own with `-extfile`, because `copyall` lets the requester dictate what the certificate claims.
Look at the request first #
You are about to vouch for whatever this says. Read it:
openssl req -in request.csr -noout -text -verify Confirm the subject is who you think it is and the requested SANs are names this requester should be allowed to have.
The quick way #
openssl x509 -req -in request.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out certificate.crt -days 397 -sha256 \
-copy_extensions copyall Replace these values
| Placeholder | What to put there |
|---|---|
-req | The input is a CSR rather than a certificate. |
-CA / -CAkey | The issuing CA certificate and its private key. |
-CAcreateserial | Create or increment the serial number file so serials stay unique. |
-copy_extensions copyall | Carry the CSR extensions across. Without this the SANs are lost. |
-days 397 | Validity. 397 days is the public-CA maximum and a reasonable internal ceiling. |
The safe way — define the extensions yourself #
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
subjectAltName = DNS:service.internal, DNS:*.service.internal openssl x509 -req -in request.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out certificate.crt -days 397 -sha256 \
-extfile ext.cnf This is how a real CA behaves: the request is an application, and the issuer decides what the certificate actually says.
What each extension does #
| Extension | Why it is there |
|---|---|
basicConstraints=CA:FALSE |
This certificate may not sign other certificates |
keyUsage |
Restricts the key to signatures and key exchange |
extendedKeyUsage=serverAuth |
Valid for TLS servers. Add clientAuth for mTLS |
subjectKeyIdentifier |
Identifies this certificate’s key |
authorityKeyIdentifier |
Links back to the issuing CA, so clients can build the chain |
subjectAltName |
The hostnames. The only thing browsers match against |
Verify what you issued #
openssl x509 -in certificate.crt -noout -subject -issuer -dates -ext subjectAltName -ext basicConstraints Then confirm it actually chains to your CA:
openssl verify -CAfile ca.crt certificate.crt Expected output
certificate.crt: OK
Building the chain file for the server #
The server needs its own certificate plus every certificate between it and the root:
cat certificate.crt ca.crt > fullchain.crt If you signed with an intermediate, the intermediate goes in the chain and the root does not — clients that trust the root do not need to be sent it.
Frequently asked questions
- Why does my signed certificate have no SANs?
- Because
openssl x509 -reqdrops all CSR extensions unless told otherwise. Add-copy_extensions copyall, or better, supply the extensions yourself with-extfile. This is the single most common mistake when running a private CA. - What does -CAcreateserial do?
- It creates a serial number file next to the CA certificate, starting at a random value, so each issued certificate gets a unique serial. After the first issuance the file exists and OpenSSL increments it. Duplicate serials from one CA break revocation and confuse clients.
- Is -copy_extensions copyall safe?
- Not entirely. It copies whatever the requester put in the CSR, including
basicConstraints CA:TRUEif they were malicious or careless — which would let their certificate sign others. For a CA that accepts requests from anyone, define the extensions yourself with-extfile. - How long should the certificate be valid?
- 397 days is the maximum public CAs may issue and a sensible internal ceiling too. Shorter validity limits the damage from a key exposure you have not noticed, and forces you to keep the renewal process working.
References
- openssl-x509(1) — -req signs a CSR with no CA database
- openssl-ca(1) — the alternative that maintains an index and serial file