Golang, sending OCSP request returns

Viewed 2336

I'm writing certificate revocation validation using OCSP request to get actual status. I send request, but getting error. ERROR: ocsp: error from server: malformed. I found error return in source: MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}, but don't understand what causes error??

func CheckRevocation(cert *x509.Certificate) error {
    var (
        buffer       []byte
        output       []byte
        OCSPResponse *ocsp.Response
        OCSPURL      string
        IssuerURL    string
        httpRequest  *http.Request
        httpResponse *http.Response
    )
    if len(cert.OCSPServer) > 0 {
        OCSPURL = cert.OCSPServer[0]
    }
    if len(cert.IssuingCertificateURL) > 0 {
        IssuerURL = cert.IssuingCertificateURL[0]
    }
    issuer, err := DownloadCert(IssuerURL) // return *x509.Certificate, err
    if err != nil {
        return err
    }
    opts := &ocsp.RequestOptions{
        Hash: crypto.SHA256,
    }
    buffer, err = ocsp.CreateRequest(cert, issuer, opts)
    if err != nil {
        return err
    }
    httpRequest, err = http.NewRequest("GET", OCSPURL, nil)
    if err != nil {
        return err
    }
    httpRequest.Header.Add("Content-Type", "application/ocsp-request")
    httpRequest.Header.Add("Accept", "application/ocsp-response")
    writer := bytes.NewBuffer(buffer)
    httpRequest.Write(writer)
    httpClient := &http.Client{}
    httpResponse, err = httpClient.Do(httpRequest)
    if err != nil {
        return err
    }
    defer httpResponse.Body.Close()
    output, err = ioutil.ReadAll(httpResponse.Body)
    if err != nil {
        return err
    }
    OCSPResponse, err = ocsp.ParseResponse(output, issuer)
    if err != nil {
        return err
    }
    debugPrint("OCSP", "result: %d", OCSPResponse.Status)
    return nil
}

EXAMPLE OF CERTIFICATE:

...
<ds:X509Certificate>
MIIG9DCCBNygAwIBAgIUTFVCYjrFFRdWjgJW0G8fUs6LLk4wDQYJKoZIhvcNAQELBQAwgc4xCzAJ
BgNVBAYTAktaMRUwEwYDVQQHDAzQkNCh0KLQkNCd0JAxFTATBgNVBAgMDNCQ0KHQotCQ0J3QkDFM
MEoGA1UECgxD0KDQnNCaIMKr0JzQldCc0JvQldCa0JXQotCi0IbQmiDQotCV0KXQndCY0JrQkNCb
0KvSmiDSmtCr0JfQnNCV0KLCuzFDMEEGA1UEAww60rDQm9Ci0KLQq9KaINCa0KPTmNCb0JDQndCU
0KvQoNCj0KjQqyDQntCg0KLQkNCb0KvSmiAoUlNBKTAeFw0xNzA3MjcwMzU1MDZaFw0xODA3Mjcw
MzU1MDZaMIHJMSwwKgYDVQQDDCPQkdCQ0JnQotCj0KDQodCr0J3QntCSINCU0JDQndCY0K/QoDEf
MB0GA1UEBAwW0JHQkNCZ0KLQo9Cg0KHQq9Cd0J7QkjEYMBYGA1UEBRMPSUlOOTIwNzI3MzAwMDQ0
MQswCQYDVQQGEwJLWjEVMBMGA1UEBwwM0JDQm9Cc0JDQotCrMRUwEwYDVQQIDAzQkNCb0JzQkNCi
0KsxIzAhBgNVBCoMGtCh0JXQmdCi0JrQkNCh0KvQnNCe0JLQmNCnMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAvW0Lyoex/kkhq5Xb5uCOtEpp4zjc34M5UGftcgmY6FXvlCaPm4415cmk
Kyaz6M+vmsK3T/wH3S2Up5cLKo022BgpBTYg6+lPUQgigYM7PPc20aLySrMg+Gu4HitokrdWgA/H
hjr14HAOFNiEZm7UpeLAhBfuAz/S9TeCct0C/dp75X8K7zOdXX1n/npnMZKMStPVJ94Cbdpprz/O
lNWzk+MGUB5+2yy4k4zBUamyEk8QzsJZID4Hva8ZIwqSXe4gyCe46iPxPZ8EKiwR4Pn6BXj0k7G1
a8A1jYEqc91QMX+L4CCS5M+BeUvbrcdMjNEoy71YG6m9qgNsBGl7WueMawIDAQABo4IByzCCAccw
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggqgw4DAwQBATAPBgNVHSMECDAG
gARVtbTiMB0GA1UdDgQWBBQKnKaePX9Y74pplb7yI5ql8zxiJDBeBgNVHSAEVzBVMFMGByqDDgMD
AgQwSDAhBggrBgEFBQcCARYVaHR0cDovL3BraS5nb3Yua3ovY3BzMCMGCCsGAQUFBwICMBcMFWh0
dHA6Ly9wa2kuZ292Lmt6L2NwczBOBgNVHR8ERzBFMEOgQaA/hh1odHRwOi8vY3JsLnBraS5nb3Yu
a3ovcnNhLmNybIYeaHR0cDovL2NybDEucGtpLmdvdi5rei9yc2EuY3JsMFIGA1UdLgRLMEkwR6BF
oEOGH2h0dHA6Ly9jcmwucGtpLmdvdi5rei9kX3JzYS5jcmyGIGh0dHA6Ly9jcmwxLnBraS5nb3Yu
a3ovZF9yc2EuY3JsMGIGCCsGAQUFBwEBBFYwVDAuBggrBgEFBQcwAoYiaHR0cDovL3BraS5nb3Yu
a3ovY2VydC9wa2lfcnNhLmNlcjAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AucGtpLmdvdi5rejAN
BgkqhkiG9w0BAQsFAAOCAgEAColZth/p4wVdcPU1AHTMhNeyLtSJI4K5A7/NoOsqYsH5qpE89gPI
mxB4NAvtmHD49f/lHLvzeIBh+snO+lfJZYXaWcYwU1+dJRyB/bE+W6vcEa5N79eihSyl9fsQTYcz
jBsXzUx0Sv2vAiZH+RIowSsg/m9+UZDt5eNcL7eqkr/JDs0I5VVYXFKwNP1rPam4jfuJKB9w/bEg
6G6RM0/DGaXxLAimVB6gLQzT1hdQzgG8YOERJxjlTeRUICUiEqFz5mZzlJ6zb2+N7lUCzRu385o1
DeWjRJZaTxFKM9JJv8a6zPjcuFA+PIsDy9GSLby1OfZaJJo/7w4iLtJiYDuyMTXisnOknmVr5LC6
fft4AXOq7zOTn7t5r3T9lb5VmorfnqdqSXrep6c/dlkSlyP2mYhUE3JOSQTraJQH/jjcWH5hfewb
/9kPWqDlTpBnok9pcciRSYOgzqkpe0CJ3ZNyj+YNuL58O+TkV9MOwe8wWrWe8rVYV61E597WJZJ/
m6qKBPYUy+zoRVK/XdYYAMsBC2zHrz8n7Z8k6s9FQo4YLgprMQh6Hcq42PgnCZZGxxJ4wU44STUj
HCtohHNYjhz+sZfKg6KEFPzGJN1O7rYd8q0Gl9vQtKMjIsETZ7BRYbXxuzMVA2CMhxiXJS929qH2
ce48JWaAdMkyzyjcF8t3tVY=
</ds:X509Certificate>

This certificate is valid, but cannot call OCSP to check it's status. May be my call is incorrect. Does anyone have ideas?

2 Answers

The standard allows for requests to made via GET or POST. See the code below for an example that works with the POST method. This example works in my company's corporate PKI and almost works with the NIST PIV test cards (https://csrc.nist.gov/projects/piv/nist-personal-identity-verification-test-cards), but trips over a bug in Go that may have been resolved in 1.11 (see https://github.com/golang/go/issues/21527)

Note that some OCSP responders require the host header. Also, both of the OSCP responders in my environment want SHA1 for the hash algorithm and fail when SHA256 is used.

func isCertificateRevokedByOCSP(commonName string, clientCert, issuerCert *x509.Certificate, ocspServer string) bool {
    opts := &ocsp.RequestOptions{Hash: crypto.SHA1}
    buffer, err := ocsp.CreateRequest(clientCert, issuerCert, opts)
    if err != nil {
        return false
    }
    httpRequest, err := http.NewRequest(http.MethodPost, ocspServer, bytes.NewBuffer(buffer))
    if err != nil {
        return false
    }
    ocspUrl, err := url.Parse(ocspServer)
    if err != nil {
        return false
    }
    httpRequest.Header.Add("Content-Type", "application/ocsp-request")
    httpRequest.Header.Add("Accept", "application/ocsp-response")
    httpRequest.Header.Add("host", ocspUrl.Host)
    httpClient := &http.Client{}
    httpResponse, err := httpClient.Do(httpRequest)
    if err != nil {
        return false
    }
    defer httpResponse.Body.Close()
    output, err := ioutil.ReadAll(httpResponse.Body)
    if err != nil {
        return false
    }
    ocspResponse, err := ocsp.ParseResponse(output, issuerCert)
    if err != nil {
        return false
    }
    if ocspResponse.Status == ocsp.Revoked {
        logger.GetLogger().Warnf("certificate '%s' has been revoked by OCSP server %s, refusing connection", commonName, ocspServer)
        return true
    } else {
        return false
    }
}

Came across this while looking for a Go example of an OCSP request. Here's the function I ended up with for anyone looking for a complete example:

// CheckOCSPStatus will make an OCSP request for the provided certificate.
// If the status of the certificate is not good, then an error is returned.
func CheckOCSPStatus(ctx context.Context, cert *x509.Certificate) error {
    var (
        ocspURL       = cert.OCSPServer[0]
        issuerCertURL = cert.IssuingCertificateURL[0]
    )

    // download the issuer certificate
    issuer, err := getCert(ctx, issuerCertURL)
    if err != nil {
        return fmt.Errorf("getting issuer certificate: %w", err)
    }

    // Build OCSP request
    buffer, err := ocsp.CreateRequest(cert, issuer, &ocsp.RequestOptions{
        Hash: crypto.SHA1,
    })
    if err != nil {
        return fmt.Errorf("creating ocsp request body: %w", err)
    }

    req, err := http.NewRequest(http.MethodPost, ocspURL, bytes.NewBuffer(buffer))
    if err != nil {
        return fmt.Errorf("creating http request: %w", err)
    }

    ocspUrl, err := url.Parse(ocspURL)
    if err != nil {
        return fmt.Errorf("parsing ocsp url: %w", err)
    }

    req.Header.Add("Content-Type", "application/ocsp-request")
    req.Header.Add("Accept", "application/ocsp-response")
    req.Header.Add("host", ocspUrl.Host)
    req = req.WithContext(ctx)

    // Make OCSP request
    httpResponse, err := http.DefaultClient.Do(req)
    if err != nil {
        return fmt.Errorf("making ocsp request: %w", err)
    }

    defer httpResponse.Body.Close()

    output, err := ioutil.ReadAll(httpResponse.Body)
    if err != nil {
        return fmt.Errorf("reading response body: %w", err)
    }

    // Parse response
    ocspResponse, err := ocsp.ParseResponse(output, issuer)
    if err != nil {
        return fmt.Errorf("parsing ocsp response: %w", err)
    }

    // check status
    if ocspResponse.Status != ocsp.Good {
        return fmt.Errorf("invalid cert status: %v", ocspResponse.Status)
    }

    return nil
}

func getCert(ctx context.Context, url string) (*x509.Certificate, error) {
    req, _ := http.NewRequest(http.MethodGet, url, nil)
    req = req.WithContext(ctx)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, fmt.Errorf("getting cert from %s: %w", url, err)
    }

    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("reading response body: %w", err)
    }

    cert, err := x509.ParseCertificate(body)
    if err != nil {
        return nil, fmt.Errorf("parsing certificate: %w", err)
    }

    return cert, nil
}

Here's a complete and runnable gist: https://gist.github.com/alexrudd/ca792642c1f7923ee344b3645a420006

Related