Some time ago, when I launched a service written in go language, I found that all HTTPS requests were suspended, and an error was reported as X509 Certificate signed by Unknown Authority, because the online environment and the test environment accessed different domain names. Therefore, this problem was not encountered in the test environment, and the online environment was accessed by Elixir at the beginning, and there was no problem, so I had to try to solve the problem by myself. This article is to record the process of solving the problem.

The solution

The first thing to do, of course, is Google, and most people’s solution is to set TLSClientConfig to skip HTTPS certificate validation. This is obviously a direct solution to the problem, but it actually avoids solving the problem, so we still give up this solution to directly find the root of the problem.

The service was originally written using Elixir, and the same HTTPS endpoint was fine. Is there something wrong with my Golang code? I started with this step, but unfortunately, it is possible to use code to access other HTTPS sites, but not the domain name of the company’s online environment, so I began to wonder if this problem is caused by the incorrect configuration of TLS online. If there is a problem with the TLS configuration of the server, then why is it ok to request another language? Why is there no error when accessing Chrome?

Ssllabs can analyze the certificate to see if there are any problems with it. When sslLabs analyzes the certificate to see if there are any problems with it, it is found that there are problems with it. After analyzing the certificate, it only gets a B score:


Then take a closer look at the following report. One of the items in the report directly indicates that there is a problem:


The COMODO RSA Domain Validation Secure Server CA certificate needs to be downloaded. There must be a problem with the server configuration, so there are two ways to solve this problem:

  1. Server fix configuration;
  2. Code to do compatibility;

Considering that the online environment is a black box for me, I’ll change it instead of bothering to change the server configuration. The change is simple:

  1. Put the missing certificate filepublic keySave as fileComodo. CRT,And then add it toDockerIn a container/usr/local/share/ca-certificates/Under the path;
  2. performupdate-ca-certificatesCommand;
  3. And you’re done.

This solution is equivalent to, you say you have a certificate, then I will add this certificate to you, there is no problem. But knowing the solution, why is there such a problem? What exactly is Chain Issues?

The root cause

First, you need to understand the concept of a certificate chain. In fact, the certificate chain constitutes the trust basis of the entire certificate. The certificate validator needs to verify the certificate chain correctly. We can use Chrome to take a look at github.com’s certificate chain:


Github.com -> DigiCert SHA2 Extended Validation Server CA -> DigiCert High Assurance EV Root CA The entire chain is divided into three certificate types, namely server entity certificates, intermediate certificates, and root certificates. The verification process based on the certificate chain is as follows:

  1. Obtain the certificate chain: The client/browser connects to an HTTPS server, and the server returns the complete certificate chain to the client/browser.
  2. Verify certificate chain relationship: the client/browser ensures that the issuer of each certificate except the same certificate is the user of its upper-level certificate. If they are inconsistent, certificate verification fails.
  3. Iterative signature verification: The client/browser obtains the certificate from the upper level of the server entity certificate (suppose certificate B)public keyTo verify the signature of the server entity certificate, and if successful, obtain it from the certificate above certificate B (suppose certificate C)public keyTo verify the signature of certificate B, and continue to iterate until the result is the same as that of the issuer and user of the root certificate.

The problem we encountered was that the HTTPS website did not deploy the certificate chain correctly when deploying the certificate, and there was a problem when obtaining the certificate chain in the first step. However, for Chrome and other development languages, if the server returns an incomplete certificate chain, they will separately replace the missing certificate. Golang does not do this, causing the entire HTTPS handshake to fail, which causes the problem.