What is a self-signed certificate in the certificate chain? Why is my OpenSSL complaining about it?

1.7K    Asked by AlexanderCoxon in SQL Server , Asked on Dec 2, 2021

I am trying to set up a certificate chain for a lab server. I have created my own root CA, an intermediate CA, and a server certificate. I supplied these certificates along with the server key to the OpenSSL s_server command. When I run OpenSSL s_client and connect to that server, OpenSSL complains that there is a self-signed certificate in the certificate chain.


When I connect to a public web server using s_client, however, not only does the server not send all of the certificates in the chain (just the intermediate parent certificate of the server certificate) but OpenSSL doesn't complain about a self-signed certificate, let alone an incomplete certificate chain.

If I use s_server with a CA file containing just the server's parent intermediate certificate, s_client complains that it can't get the local issuer certificate. I never see this error with public web servers even though they don't send the entire certificate chain.


In none of these tests (using my own certificates or public web servers) I am using the -CApath, -CAfile or -verify options with the s_client command.

I don't know what I'm doing wrong. Why does s_client complain about my self-signed certificate in the certificate chain even though I don't use -verify? Why is it complaining about my (I assume) root certificate being self-signed when all root certificates are self-signed? For example, this is what I get with a public web server:


openssl s_client -showcerts -servername security.stackexchange.com -connect security.stackexchange.com:443 CONNECTED(00000004) depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify return:1 depth=0 CN = *.stackexchange.com verify return:1 ---

But using s_server with my full certificate chain, I get this:


openssl s_client -showcerts -servername server.domain.com -connect server.domain.com:443 CONNECTED(00000004) depth=2 C = US, ST = State, L = City, O = Company, OU = Company CA verify error:num=19:self signed certificate in certificate chain


Here are my certificates. And yes, I have the constraints CA=TRUE, Digital Signature and Certificate Sign set in my root CA.


root CA:

intermediate CA:

server certificate:


Answered by Anisha Dalal

First, it is quite correct for a TLS/SSL server to send the intermediate aka chain cert(s) but not the root cert; see rfc 5246 sec 7.4.2 or the slightly more verbose version in rfc 8446 sec 4.4.2.

As Z.T. mostly correctly commented, -verify is the default for s_client (you don't need to specify it) and if you don't specify -CAfile and/or -CApath by default (except for bugs in some obsolete versions) it uses a default trust store, configured at compile-time, which can include either a single file with concatenated PEM certs or a directory containing separate PEM files with names or links using truncated subject hashes usually created by c_rehash (add) or, as commented, manually determined with x509 -hash; see the man page for verify(1ssl) on your system or on the web. OpenSSL itself doesn't provide the root certs that go in such a truststore; if you are using a distro or other packaged version, the builder usually configures OpenSSL to use a set of root certs provided by the distro or package. (On distros I know, this distro-provided truststore is also used for other software including NSS, GNUtls, and Java; see below.) If you (or someone) built OpenSSL by hand, you have to do this yourself. Since you are not getting a verify error on public servers, your build presumably is using a truststore that has (at least some) public CA's preinstalled; you can see where this is with openssl version -d (plus the hardcoded names cert.pem and/or certs).

You can add your root or other anchor (see below) to this default truststore, or use -CAfile and/or -CApath to specify a custom one containing your own root(s) or anchor(s). On the distros, I know, and probably others, you should NOT hand-modify the default files because they are automatically generated by a process that also sets the truststores used by other software such as NSS, GNUtls, and Java; these need to contain the same data (certs) but in different formats. Instead, see e.g. man update-ca-trust for RedHat family or man update-ca-certificates for Debian family.

OpenSSL will use an intermediate (aka chain) cert or certs in the truststore to build the cert chain if needed, i.e. if not sent by the server (in violation of the RFC, but many do that), but historically it will only accept a chain -- either fully received from the server or (partly) built from the local truststore -- if it ends at a root that is in the local truststore. For recent versions, namely 1.0.2 up but only documented since 1.1.0, there is an option -partial_chain which does accept a chain ending in an intermediate (non-root) that is in the local truststore. How to fix a self-signed certificate in the certificate chain?

For Node.js

You can insert an environment variable to allow untrusted certificates using the following command at the beginning of the code:

  process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;

This is risky and it’s not recommended to be used in production. Alternatively, use npm config set strict-ssl=false if you have to do this for many applications and you want to save repeating the process. Users also suggest upgrading your version of Node, to fixes any existing bugs and vulnerabilities.

For npm

The recommended solution is, again, to upgrade your version of npm running one of the following:

npm install npm -g --ca=null 
npm update npm -g

Or, tell your current version of npm to use known registrars, and after installing, stop using them:

npm config set ca ""

npm install npm -g

npm config delete ca

Some users mentioned that they only switched the registry URL from https to http:

npm config set registry="http://registry.npmjs.org/"



Your Answer

Answer (1)

A self-signed certificate in the certificate chain typically means that one of the certificates in the chain is signed by the same entity that issued it, rather than by a trusted Certificate Authority (CA). In a typical SSL/TLS certificate chain, each certificate is issued by a CA and is supposed to be verifiable against a higher-level CA until the chain reaches a trusted root CA. A self-signed certificate, on the other hand, does not provide this external validation.


Why OpenSSL Complains About It

When OpenSSL complains about a self-signed certificate in the certificate chain, it is usually due to one of the following reasons:

  1. Untrusted Certificate: The self-signed certificate is not included in the list of trusted certificates (often found in the system’s CA bundle). OpenSSL cannot verify the certificate against a trusted root CA.
  2. Incorrect Chain Order: The order of the certificates in the chain might be incorrect. The certificates should be presented in the correct order, starting from the server certificate followed by intermediate certificates, and ending with the root certificate.
  3. Missing Intermediate Certificates: If any intermediate certificate is missing, OpenSSL may not be able to establish a complete chain of trust from the server certificate to a trusted root CA.
  4. Expired or Invalid Certificates: One or more certificates in the chain might be expired or otherwise invalid.

How to Diagnose and Fix the Issue

1. Verify the Certificate Chain

Use OpenSSL to inspect the certificate chain and identify where the problem lies. You can use the following command to verify a certificate against the specified CA bundle:

openssl verify -CAfile path/to/ca-bundle.crt path/to/certificate.crt

2. Check for Self-Signed Certificate

To check if a certificate is self-signed, use:

openssl x509 -in path/to/certificate.crt -noout -issuer -subject

If the issuer and subject fields are identical, the certificate is self-signed.

3. Include All Intermediate Certificates

Ensure that all intermediate certificates are included in the correct order. You can create a concatenated file containing the server certificate and all intermediate certificates:

cat server.crt intermediate1.crt intermediate2.crt > fullchain.crt

4. Add the Self-Signed Certificate to the Trusted Store

If the self-signed certificate is a trusted root CA within your organization or context, add it to the CA bundle:

cat selfsigned.crt >> /etc/ssl/certs/ca-bundle.crt

Example of a Complete Verification Process

Here’s a step-by-step example assuming you have a server certificate (server.crt), an intermediate certificate (intermediate.crt), and a self-signed root certificate (root.crt):

Combine Certificates into One File:

  cat server.crt intermediate.crt > chained.crt

Verify the Combined Certificate Against the Root CA:

If OpenSSL does not produce any errors, the chain is correctly verified. If there are errors, they will provide hints as to what needs to be corrected.

Summary

A self-signed certificate in the certificate chain can cause OpenSSL to complain if it cannot establish a chain of trust. Ensure that all intermediate certificates are included, the order is correct, and, if necessary, add the self-signed certificate to the trusted CA bundle. By properly configuring and verifying your certificate chain, you can resolve OpenSSL complaints and ensure secure communication.





6 Months

Interviews

Parent Categories