首页 > 解决方案 > Golang X509 密钥对被忽略

问题描述

我们正在使用 Golang 与使用 HTTPS 的外部服务进行通信。当使用 cURL 尝试请求时,我们获得了成功。使用 Go 时,证书似乎被忽略,从外部服务产生 403。

我们无法发现 cURL 请求与 Golang 代码的任何差异。有人可以帮助我们找到不同之处吗?

cURL-request 为我们提供了正确的 JSON 响应。Go 代码给出:

2020/09/07 15:05:57 request error: perform request: api response: 403 Forbidden

工作 cURL 请求(用于调试目的的用户代理):

curl -X GET --http1.1 -i -v --key client.key.pem --cacert ca.pem --cert client.pem "https://[redacted]/path/to/endpoint" -H "Accept: application/json; charset=utf-8" -H "User-Agent: Apache-HttpClient/4.5.5 (Java/12.0.1)" -H "X-Identifier: [redacted]" -H "Accept-Encoding: gzip, deflate" -H "Connection: Keep-Alive"

产生 403 的 Golang 代码:(注意:文件ca.pemclient.pem(证书)并且client.key.pem必须在同一目录中。运行脚本为go run catest.go --url "https://[redacted]/path/to/endpoint" --identifier [redacted]

package main

import (
    "crypto/tls"
    "crypto/x509"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {
    // Get URL to call.
    url := flag.String("url", "", "URL to call")
    identifier := flag.String("identifier", "", "the X-Identifier value")
    flag.Parse()

    if url == nil || identifier == nil || *url == "" || *identifier == "" {
        log.Fatal("'url' and 'identifier' arguments must be provided")
    }

    // Set up certificates
    caPEM, err := ioutil.ReadFile("ca.pem")
    if err != nil {
        log.Fatalf("unable to read 'ca.pem' in current directory: %v", err)
    }

    clientPEM, err := ioutil.ReadFile("client.pem")
    if err != nil {
        log.Fatalf("unable to read 'client.pem' in current directory: %v", err)
    }

    clientKeyPEM, err := ioutil.ReadFile("client.key.pem")
    if err != nil {
        log.Fatalf("unable to read 'client.key.pem' in current directory: %v", err)
    }

    // Make calls.
    client, err := configureClient(caPEM, clientPEM, clientKeyPEM)
    if err != nil {
        log.Fatalf("unable to setup client: %v", err)
    }

    _, err = performRequest(client, *url, *identifier)
    if err != nil {
        log.Fatalf("request error: %v", err)
    }

    log.Printf("request successful")
}

func configureClient(caCertPEM, clientCertPEM, clientKeyPEM []byte) (*http.Client, error) {
    // Load the CA certificate.
    caCertPool, err := x509.SystemCertPool()
    if err != nil {
        return nil, fmt.Errorf("configure client: load cert pool: %w", err)
    }

    // Append root CA cert from parameter
    ok := caCertPool.AppendCertsFromPEM(caCertPEM)
    if !ok {
        return nil, fmt.Errorf("configure client: could not append ca certificate")
    }

    // Load the client certificate.
    clientCert, err := tls.X509KeyPair(clientCertPEM, clientKeyPEM)
    if err != nil {
        return nil, fmt.Errorf("configure client: load client certificate: %w", err)
    }

    // Setup HTTPS client.
    tlsConfig := &tls.Config{
        RootCAs:       caCertPool,
        Certificates:  []tls.Certificate{clientCert},
        Renegotiation: tls.RenegotiateOnceAsClient,
    }
    tlsConfig.BuildNameToCertificate()

    transport := &http.Transport{TLSClientConfig: tlsConfig}
    client := &http.Client{Transport: transport}

    return client, nil
}

func performRequest(client *http.Client, u, identifier string) ([]byte, error) {
    if client == nil {
        return nil, fmt.Errorf("perform request: nil client")
    }

    // Prepare request
    req, err := http.NewRequest(http.MethodGet, u, nil)
    if err != nil {
        return nil, fmt.Errorf("perform request: create GET request: %w", err)
    }

    // Add same headers as cURL.
    req.Header.Add("Accept", "application/json; charset=utf-8")
    req.Header.Add("User-Agent", "Apache-HttpClient/4.5.5 (Java/12.0.1)")
    req.Header.Add("Accept-Encoding", "gzip, deflate")
    req.Header.Add("Connection", "Keep-Alive")
    req.Header.Add("X-Identifier", identifier)

    // Send request
    resp, err := client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("perform request: client do: %w", err)
    }
    defer resp.Body.Close()

    switch resp.StatusCode {
    case http.StatusOK:
        break
    case http.StatusUnauthorized:
        return nil, fmt.Errorf("perform request: api response: unauthorized")
    case http.StatusBadRequest:
        return nil, fmt.Errorf("perform request: api response: bad request")
    default:
        return nil, fmt.Errorf("perform request: api response: %v", resp.Status)
    }

    data, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("perform request: read response body: %w", err)
    }

    return data, nil
}

标签: gossl

解决方案


推荐阅读