29

I am creating a client app using Golang 1.9.2 and I am having some trouble to access my backend. The thing is that my app is working fine in the latest versions of Windows and Linux, however when I run it on Windows XP (yes, unfortunately I do have to support Windows XP, because some of our customers refuse to upgrade their OS) I get this error while trying to execute an HTTP GET and an HTTP POST: x509: certificate signed by unknown authority.

I've ran the same GET command using a Firefox ESR browser and a Chromium browser, from inside the Windows XP and none of them complain about the certificate.

Please note that my certificate is valid and signed by a trusted authority.

I've done some research and I found out that some people had the same problem and solved it by ignoring the TLS validation using this:

import ("net/http"; "crypto/tls")

tr := &http.Transport{
    TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://someurl:443/)

So I added this to my code, but it is still not working:

// NewAPIClient - creates a new API client
func NewAPIClient() Client {
    c := &APIClient{}

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkyVerify: true},
    }
    c.client = &http.Client{Transport: tr}
    return c
}

// GetTasks - retrieves a list of tasks from the backend.
func (c *APIClient) GetTasks() ([]byte, error) {
    conf := config.GetInstance()
    url := fmt.Sprintf("%s/myurl", conf.GetConfig().APIUrl)

    req, err := http.NewRequest(http.MethodGet, url, nil)
    if err != nil {
        log.WithError(err).Errorf("Error creating HTTP request")
        return nil, err
    }

    // Add headers
    req.Header.Add("Authorization", conf.GetConfig().APIToken)
    req.Header.Add("Accept", "application/json")

    log.Info("Retrieving tasks from the API")
    resp, err := c.client.Do(req)
    if err != nil {
        log.WithError(err).Errorf("Error retrieving tasks from the backend")
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != 200 {
        errMsg := fmt.Sprintf("Received status: %s", resp.Status)
        err = errors.New(errMsg)
        log.WithError(err).Error("Error retrieving tasks from the backend")
        return nil, err
    }

    tasks, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.WithError(err).Error("Error reading tasks response body")
        return nil, err
    }

    log.Info("The tasks were successfully retrieved")

    return tasks, nil
}

Is there a another way to solve this problem, without having to ignore the certificate validation? If not, what I am doing wrong in my code?

Felipe
  • 6,312
  • 11
  • 52
  • 70
  • 14
    This is most likely because the system's certificate pool is heavily out of date, which is expected since Windows XP support has been completely dropped by Microsoft. You need to manually add the authority's certificates to a copy of the system's cert pool, as well as automatically keeping them up to date when they expire or are replaced. You can use `pool, err := x509.SystemCertPool()` and then the pool's `AddCert()` function to add the CA's certs, and finally use that pool in your TLS config for the requests. [More info](https://golang.org/pkg/crypto/x509/#SystemCertPool). – Sven Nov 23 '17 at 23:15
  • Hi, I've tried to install the certificates manually in Windows (by double-clicking them), but it didn't work. I will try your suggestion next. I have just one question: when I add the certificates using the `AddCert()`, will they be permanently added or will I have to do this every time my app runs? – Felipe Nov 24 '17 at 12:59
  • Every time your application runs. The `x509.SystemCertPool()` function returns a *copy* of the system's certificate pool, and any mutations to it is only held in-memory and not written to disk. This is why you need to setup a secure way to automatically update the certificates when they expire. It's quite a bit of work just to get Windows XP support, to be honest. This is what web browsers do, they keep their own certificate pool, and ignores the system's certificate pool. – Sven Nov 24 '17 at 13:08
  • 2
    Here's how to add a certificate to the certificate pool: https://stackoverflow.com/a/38825553/1696153 – Katie Apr 26 '19 at 21:08
  • See this answer if you are using docker https://stackoverflow.com/a/60909235/2670370 – Shubham Chaudhary Apr 01 '20 at 19:48
  • 4
    `{InsecureSkipVerify : true}` is a hack. I had same problem as you a while back, though reasons were different, and found this skip as suggested solution in couple of places. This shouldn't fly in production code. – Tomasz Giba May 26 '20 at 12:18

2 Answers2

8

You are doing this:

// NewAPIClient - creates a new API client
func NewAPIClient() Client {
    c := &APIClient{}

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkyVerify: true}, // <--- Problem
    }
    c.client = &http.Client{Transport: tr}
    return c
}

But it is InsecureSkipVerify instead of InsecureSkyVerify.

Be careful however, because InsecureSkipVerify controls whether a client verifies the server's certificate chain and hostname. If InsecureSkipVerify is true, crypto/tls accepts any certificate presented by the server and any hostname in that certificate. In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is used. This should be used only for testing or in combination with VerifyConnection or VerifyPeerCertificate.

leonheess
  • 16,068
  • 14
  • 77
  • 112
3

Golang uses the OS certificate store. The following comment indicates Go uses the Windows store on Windows, similar to Linux.

// CertGetCertificateChain will traverse Windows's root stores in an attempt to build a verified certificate chain

This comment and the associated code is in the following file:

https://golang.org/src/crypto/x509/root_windows.go

Add the server certificate, Intermediate CA certificate and/or Root CA certificate to the Windows XP certificate store. You can use the following Windows XP instructions posted by IBM:

Procedure

  1. From Windows XP, select Start > Run to open the command line.
  2. Type mmc into the Run dialog box and click OK to run the Microsoft Management Console (MMC).
  3. From within MMC, select File > Add/Remove Snap-in.
  4. Click Add.
  5. Click Certificates.
  6. Click My user account.
  7. Click Finish.
  8. Click Close on the Add Standalone Snap-in dialog box.
  9. Click OK on the Add/Remove Snap-in dialog box.

Ref: https://www.ibm.com/docs/en/b2b-integrator/5.2?topic=xp-install-root-certificate-in-windows

GlobalSign and Securely provide similar instructions for more modern versions of Windows but the IBM link above is specifically for Windows XP. The Securely docs below also include screen shots.

Grokify
  • 15,092
  • 6
  • 60
  • 81