diff --git a/builtin/credential/cert/backend_test.go b/builtin/credential/cert/backend_test.go index d56bd25a45..6260c368e8 100644 --- a/builtin/credential/cert/backend_test.go +++ b/builtin/credential/cert/backend_test.go @@ -2346,6 +2346,98 @@ func TestBackend_CertUpgrade(t *testing.T) { } } +// TestOCSPFailOpenWithBadIssuer validates we fail all different types of cert auth +// login scenarios if we encounter an OCSP verification error +func TestOCSPFailOpenWithBadIssuer(t *testing.T) { + caFile := "test-fixtures/root/rootcacert.pem" + pemCa, err := os.ReadFile(caFile) + require.NoError(t, err, "failed reading in file %s", caFile) + caTLS := loadCerts(t, caFile, "test-fixtures/root/rootcakey.pem") + leafTLS := loadCerts(t, "test-fixtures/keys/cert.pem", "test-fixtures/keys/key.pem") + + rootConfig := &rootcerts.Config{ + CAFile: caFile, + } + rootCAs, err := rootcerts.LoadCACerts(rootConfig) + connState, err := testConnStateWithCert(leafTLS, rootCAs) + require.NoError(t, err, "error testing connection state: %v", err) + + badCa, badCaKey := createCa(t) + + // Setup an OCSP handler + ocspHandler := func(ca *x509.Certificate, caKey crypto.Signer) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + now := time.Now() + ocspRes := ocsp.Response{ + SerialNumber: leafTLS.Leaf.SerialNumber, + ThisUpdate: now.Add(-1 * time.Hour), + NextUpdate: now.Add(30 * time.Minute), + Status: ocsp.Good, + } + response, err := ocsp.CreateResponse(ca, ca, ocspRes, caKey) + if err != nil { + t.Fatalf("failed generating OCSP response: %v", err) + } + _, _ = w.Write(response) + }) + } + goodTs := httptest.NewServer(ocspHandler(caTLS.Leaf, caTLS.PrivateKey.(crypto.Signer))) + badTs := httptest.NewServer(ocspHandler(badCa, badCaKey)) + defer goodTs.Close() + defer badTs.Close() + + steps := []logicaltest.TestStep{ + // step 1/2: This should fail as we get a response from a bad root, even with ocsp_fail_open is set to true + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", allowed{names: "cert.example.com"}, false, + map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{badTs.URL}, + "ocsp_query_all_servers": false, + "ocsp_fail_open": true, + }), + testAccStepLoginInvalid(t, connState), + // step 3/4: This should fail as we query all the servers which will get a response with an invalid signature + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", allowed{names: "cert.example.com"}, false, + map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{goodTs.URL, badTs.URL}, + "ocsp_query_all_servers": true, + "ocsp_fail_open": true, + }), + testAccStepLoginInvalid(t, connState), + // step 5/6: This should fail as we will query the OCSP server with the bad root key first. + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", allowed{names: "cert.example.com"}, false, + map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{badTs.URL, goodTs.URL}, + "ocsp_query_all_servers": false, + "ocsp_fail_open": true, + }), + testAccStepLoginInvalid(t, connState), + // step 7/8: This should pass as we will only query the first server with the valid root signature + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", allowed{names: "cert.example.com"}, false, + map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{goodTs.URL, badTs.URL}, + "ocsp_query_all_servers": false, + "ocsp_fail_open": true, + }), + testAccStepLogin(t, connState), + } + + // Setup a new factory everytime to avoid OCSP caching from influencing the test + for i := 0; i < len(steps); i += 2 { + setup := i + execute := i + 1 + t.Run(fmt.Sprintf("steps-%d-%d", setup+1, execute+1), func(t *testing.T) { + logicaltest.Test(t, logicaltest.TestCase{ + CredentialBackend: testFactory(t), + Steps: []logicaltest.TestStep{steps[setup], steps[execute]}, + }) + }) + } +} + // TestOCSPWithMixedValidResponses validates the expected behavior of multiple OCSP servers configured, // with and without ocsp_query_all_servers enabled or disabled. func TestOCSPWithMixedValidResponses(t *testing.T) { @@ -2402,6 +2494,14 @@ func TestOCSPWithMixedValidResponses(t *testing.T) { "ocsp_query_all_servers": false, }), testAccStepLoginInvalid(t, connState), + // step 5/6: This should fail as we will query all the OCSP servers and prefer the revoke response + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", + allowed{names: "cert.example.com"}, false, map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{goodTs.URL, revokeTs.URL}, + "ocsp_query_all_servers": true, + }), + testAccStepLoginInvalid(t, connState), } // Setup a new factory everytime to avoid OCSP caching from influencing the test @@ -2496,6 +2596,28 @@ func TestOCSPFailOpenWithGoodResponse(t *testing.T) { "ocsp_max_retries": 0, }), testAccStepLogin(t, connState), + // Step 9/10 With a single positive response, query all servers set to true and fail open true, pass validation + // as fail open is true + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", allowed{names: "cert.example.com"}, false, + map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{ts.URL, "http://127.0.0.1:30001"}, + "ocsp_fail_open": true, + "ocsp_query_all_servers": true, + "ocsp_max_retries": 0, + }), + testAccStepLogin(t, connState), + // Step 11/12 With a single positive response, query all servers set to true and fail open false, fail validation + // as not all servers agree + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", + allowed{names: "cert.example.com"}, false, map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{ts.URL, "http://127.0.0.1:30001"}, + "ocsp_fail_open": false, + "ocsp_query_all_servers": true, + "ocsp_max_retries": 0, + }), + testAccStepLoginInvalid(t, connState), } // Setup a new factory everytime to avoid OCSP caching from influencing the test @@ -2567,6 +2689,26 @@ func TestOCSPFailOpenWithRevokeResponse(t *testing.T) { "ocsp_max_retries": 0, }), testAccStepLoginInvalid(t, connState), + // Step 5/6 With a single revoke response, query all servers set to true and fail open false, fail validation + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", + allowed{names: "cert.example.com"}, false, map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{ts.URL, "http://127.0.0.1:30001"}, + "ocsp_fail_open": false, + "ocsp_query_all_servers": true, + "ocsp_max_retries": 0, + }), + testAccStepLoginInvalid(t, connState), + // Step 7/8 With a single revoke response, query all servers set to true and fail open true, fail validation + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", allowed{names: "cert.example.com"}, false, + map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{ts.URL, "http://127.0.0.1:30001"}, + "ocsp_fail_open": true, + "ocsp_query_all_servers": true, + "ocsp_max_retries": 0, + }), + testAccStepLoginInvalid(t, connState), } // Setup a new factory everytime to avoid OCSP caching from influencing the test @@ -2638,6 +2780,26 @@ func TestOCSPFailOpenWithUnknownResponse(t *testing.T) { "ocsp_max_retries": 0, }), testAccStepLoginInvalid(t, connState), + // Step 5/6 With a single unknown response, query all servers set to true and fail open true, fail validation + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", allowed{names: "cert.example.com"}, false, + map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{ts.URL, "http://127.0.0.1:30001"}, + "ocsp_fail_open": true, + "ocsp_query_all_servers": true, + "ocsp_max_retries": 0, + }), + testAccStepLogin(t, connState), + // Step 7/8 With a single unknown response, query all servers set to true and fail open false, fail validation + testAccStepCertWithExtraParams(t, "web", pemCa, "foo", + allowed{names: "cert.example.com"}, false, map[string]interface{}{ + "ocsp_enabled": true, + "ocsp_servers_override": []string{ts.URL, "http://127.0.0.1:30001"}, + "ocsp_fail_open": false, + "ocsp_query_all_servers": true, + "ocsp_max_retries": 0, + }), + testAccStepLoginInvalid(t, connState), } // Setup a new factory everytime to avoid OCSP caching from influencing the test @@ -2751,3 +2913,28 @@ func loadCerts(t *testing.T, certFile, certKey string) tls.Certificate { return caTLS } + +func createCa(t *testing.T) (*x509.Certificate, *ecdsa.PrivateKey) { + rootCaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err, "failed generated root key for CA") + + // Validate we reject CSRs that contain CN that aren't in the original order + cr := &x509.Certificate{ + Subject: pkix.Name{CommonName: "Root Cert"}, + SerialNumber: big.NewInt(1), + IsCA: true, + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + NotBefore: time.Now().Add(-1 * time.Second), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}, + } + rootCaBytes, err := x509.CreateCertificate(rand.Reader, cr, cr, &rootCaKey.PublicKey, rootCaKey) + require.NoError(t, err, "failed generating root ca") + + rootCa, err := x509.ParseCertificate(rootCaBytes) + require.NoError(t, err, "failed parsing root ca") + + return rootCa, rootCaKey +} diff --git a/builtin/credential/cert/path_login.go b/builtin/credential/cert/path_login.go index 55f161a9fe..33f62818ec 100644 --- a/builtin/credential/cert/path_login.go +++ b/builtin/credential/cert/path_login.go @@ -687,6 +687,10 @@ func (b *backend) checkForCertInOCSP(ctx context.Context, clientCert *x509.Certi defer b.ocspClientMutex.RUnlock() err := b.ocspClient.VerifyLeafCertificate(ctx, clientCert, chain[1], conf) if err != nil { + if ocsp.IsOcspVerificationError(err) { + // We don't want anything to override an OCSP verification error + return false, err + } if conf.OcspFailureMode == ocsp.FailOpenTrue { onlyNetworkErrors := b.handleOcspErrorInFailOpen(err) if onlyNetworkErrors { diff --git a/changelog/26091.txt b/changelog/26091.txt new file mode 100644 index 0000000000..31a219247a --- /dev/null +++ b/changelog/26091.txt @@ -0,0 +1,3 @@ +```release-note:security +auth/cert: validate OCSP response was signed by the expected issuer and serial number matched request +``` diff --git a/sdk/helper/ocsp/client.go b/sdk/helper/ocsp/client.go index c55f109a7e..f42ec6d277 100644 --- a/sdk/helper/ocsp/client.go +++ b/sdk/helper/ocsp/client.go @@ -76,6 +76,15 @@ const ( cacheExpire = float64(24 * 60 * 60) ) +// ErrOcspIssuerVerification indicates an error verifying the identity of an OCSP response occurred +type ErrOcspIssuerVerification struct { + Err error +} + +func (e *ErrOcspIssuerVerification) Error() string { + return fmt.Sprintf("ocsp response verification error: %v", e.Err) +} + type ocspCachedResponse struct { time float64 producedAt float64 @@ -308,6 +317,7 @@ func (c *Client) retryOCSP( ocspHost *url.URL, headers map[string]string, reqBody []byte, + subject, issuer *x509.Certificate, ) (ocspRes *ocsp.Response, ocspResBytes []byte, ocspS *ocspStatus, retErr error) { doRequest := func(request *retryablehttp.Request) (*http.Response, error) { @@ -379,66 +389,19 @@ func (c *Client) retryOCSP( continue } - // Above, we use the unsafe issuer=nil parameter to ocsp.ParseResponse - // because Go's library does the wrong thing. - // - // Here, we lack a full chain, but we know we trust the parent issuer, - // so if the Go library incorrectly discards useful certificates, we - // likely cannot verify this without passing through the full chain - // back to the root. - // - // Instead, take one of two paths: 1. if there is no certificate in - // the ocspRes, verify the OCSP response directly with our trusted - // issuer certificate, or 2. if there is a certificate, either verify - // it directly matches our trusted issuer certificate, or verify it - // is signed by our trusted issuer certificate. - // - // See also: https://github.com/golang/go/issues/59641 - // - // This addresses the !!unsafe!! behavior above. - if ocspRes.Certificate == nil { - if err := ocspRes.CheckSignatureFrom(issuer); err != nil { - err = fmt.Errorf("error directly verifying signature on %v OCSP response: %w", method, err) - retErr = multierror.Append(retErr, err) - continue - } - } else { - // Because we have at least one certificate here, we know that - // Go's ocsp library verified the signature from this certificate - // onto the response and it was valid. Now we need to know we trust - // this certificate. There's two ways we can do this: - // - // 1. Via confirming issuer == ocspRes.Certificate, or - // 2. Via confirming ocspRes.Certificate.CheckSignatureFrom(issuer). - if !bytes.Equal(issuer.Raw, ocspRes.Raw) { - // 1 must not hold, so 2 holds; verify the signature. - if err := ocspRes.Certificate.CheckSignatureFrom(issuer); err != nil { - err = fmt.Errorf("error checking chain of trust on %v OCSP response via %v failed: %w", method, issuer.Subject.String(), err) - retErr = multierror.Append(retErr, err) - continue - } + if err := validateOCSPParsedResponse(ocspRes, subject, issuer); err != nil { + err = fmt.Errorf("error validating %v OCSP response: %w", method, err) - // Verify the OCSP responder certificate is still valid and - // contains the required EKU since it is a delegated OCSP - // responder certificate. - if ocspRes.Certificate.NotAfter.Before(time.Now()) { - err := fmt.Errorf("error checking delegated OCSP responder on %v OCSP response: certificate has expired", method) - retErr = multierror.Append(retErr, err) - continue - } - haveEKU := false - for _, ku := range ocspRes.Certificate.ExtKeyUsage { - if ku == x509.ExtKeyUsageOCSPSigning { - haveEKU = true - break - } - } - if !haveEKU { - err := fmt.Errorf("error checking delegated OCSP responder on %v OCSP response: certificate lacks the OCSP Signing EKU", method) - retErr = multierror.Append(retErr, err) - continue - } + if IsOcspVerificationError(err) { + // We want to immediately give up on a verification error to a response + // and inform the user something isn't correct + return nil, nil, nil, err } + + retErr = multierror.Append(retErr, err) + // Clear the response out as we can't trust it. + ocspRes = nil + continue } // While we haven't validated the signature on the OCSP response, we @@ -473,6 +436,80 @@ func (c *Client) retryOCSP( return } +func IsOcspVerificationError(err error) bool { + errOcspIssuer := &ErrOcspIssuerVerification{} + return errors.As(err, &errOcspIssuer) +} + +func validateOCSPParsedResponse(ocspRes *ocsp.Response, subject, issuer *x509.Certificate) error { + // Above, we use the unsafe issuer=nil parameter to ocsp.ParseResponse + // because Go's library does the wrong thing. + // + // Here, we lack a full chain, but we know we trust the parent issuer, + // so if the Go library incorrectly discards useful certificates, we + // likely cannot verify this without passing through the full chain + // back to the root. + // + // Instead, take one of two paths: 1. if there is no certificate in + // the ocspRes, verify the OCSP response directly with our trusted + // issuer certificate, or 2. if there is a certificate, either verify + // it directly matches our trusted issuer certificate, or verify it + // is signed by our trusted issuer certificate. + // + // See also: https://github.com/golang/go/issues/59641 + // + // This addresses the !!unsafe!! behavior above. + if ocspRes.Certificate == nil { + if err := ocspRes.CheckSignatureFrom(issuer); err != nil { + return &ErrOcspIssuerVerification{fmt.Errorf("error directly verifying signature: %w", err)} + } + } else { + // Because we have at least one certificate here, we know that + // Go's ocsp library verified the signature from this certificate + // onto the response and it was valid. Now we need to know we trust + // this certificate. There's two ways we can do this: + // + // 1. Via confirming issuer == ocspRes.Certificate, or + // 2. Via confirming ocspRes.Certificate.CheckSignatureFrom(issuer). + if !bytes.Equal(issuer.Raw, ocspRes.Raw) { + // 1 must not hold, so 2 holds; verify the signature. + if err := ocspRes.Certificate.CheckSignatureFrom(issuer); err != nil { + return &ErrOcspIssuerVerification{fmt.Errorf("error checking chain of trust %v failed: %w", issuer.Subject.String(), err)} + } + + // Verify the OCSP responder certificate is still valid and + // contains the required EKU since it is a delegated OCSP + // responder certificate. + if ocspRes.Certificate.NotAfter.Before(time.Now()) { + return &ErrOcspIssuerVerification{fmt.Errorf("error checking delegated OCSP responder OCSP response: certificate has expired")} + } + haveEKU := false + for _, ku := range ocspRes.Certificate.ExtKeyUsage { + if ku == x509.ExtKeyUsageOCSPSigning { + haveEKU = true + break + } + } + if !haveEKU { + return &ErrOcspIssuerVerification{fmt.Errorf("error checking delegated OCSP responder: certificate lacks the OCSP Signing EKU")} + } + } + } + + // Verify the response was for our original subject + if ocspRes.SerialNumber == nil || subject.SerialNumber == nil { + return &ErrOcspIssuerVerification{fmt.Errorf("OCSP response or cert did not contain a serial number")} + } + if ocspRes.SerialNumber.Cmp(subject.SerialNumber) != 0 { + return &ErrOcspIssuerVerification{fmt.Errorf( + "OCSP response serial number %s did not match the leaf certificate serial number %s", + certutil.GetHexFormatted(ocspRes.SerialNumber.Bytes(), ":"), + certutil.GetHexFormatted(subject.SerialNumber.Bytes(), ":"))} + } + + return nil +} + // GetRevocationStatus checks the certificate revocation status for subject using issuer certificate. func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate, conf *VerifyConfig) (*ocspStatus, error) { status, ocspReq, encodedCertID, err := c.validateWithCache(subject, issuer, conf) @@ -520,12 +557,12 @@ func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509. ocspClient.HTTPClient.Timeout = timeout ocspClient.HTTPClient.Transport = newInsecureOcspTransport(conf.ExtraCas) - doRequest := func() error { + doRequest := func(i int) error { if conf.QueryAllServers { defer wg.Done() } ocspRes, _, ocspS, err := c.retryOCSP( - ctx, ocspClient, retryablehttp.NewRequest, u, headers, ocspReq, issuer) + ctx, ocspClient, retryablehttp.NewRequest, u, headers, ocspReq, subject, issuer) ocspResponses[i] = ocspRes if err != nil { errors[i] = err @@ -553,9 +590,9 @@ func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509. } if conf.QueryAllServers { wg.Add(1) - go doRequest() + go doRequest(i) } else { - err = doRequest() + err = doRequest(i) if err == nil { break } @@ -570,6 +607,9 @@ func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509. var firstError error for i := range ocspHosts { if errors[i] != nil { + if IsOcspVerificationError(errors[i]) { + return nil, errors[i] + } if firstError == nil { firstError = errors[i] } diff --git a/sdk/helper/ocsp/ocsp_test.go b/sdk/helper/ocsp/ocsp_test.go index 9aa1d7c9f8..326a5b2336 100644 --- a/sdk/helper/ocsp/ocsp_test.go +++ b/sdk/helper/ocsp/ocsp_test.go @@ -298,11 +298,14 @@ func TestUnitValidOCSPResponse(t *testing.T) { // OCSP response conditions func TestUnitBadOCSPResponses(t *testing.T) { rootCaKey, rootCa, leafCert := createCaLeafCerts(t) + rootCaKey2, rootCa2, _ := createCaLeafCerts(t) type tests struct { name string ocspRes ocsp.Response maxAge time.Duration + ca *x509.Certificate + caKey *ecdsa.PrivateKey errContains string } @@ -310,6 +313,30 @@ func TestUnitBadOCSPResponses(t *testing.T) { ctx := context.Background() tt := []tests{ + { + name: "bad-signing-issuer", + ocspRes: ocsp.Response{ + SerialNumber: leafCert.SerialNumber, + ThisUpdate: now.Add(-1 * time.Hour), + NextUpdate: now.Add(30 * time.Minute), + Status: ocsp.Good, + }, + ca: rootCa2, + caKey: rootCaKey2, + errContains: "error directly verifying signature", + }, + { + name: "incorrect-serial-number", + ocspRes: ocsp.Response{ + SerialNumber: big.NewInt(1000), + ThisUpdate: now.Add(-1 * time.Hour), + NextUpdate: now.Add(30 * time.Minute), + Status: ocsp.Good, + }, + ca: rootCa, + caKey: rootCaKey, + errContains: "did not match the leaf certificate serial number", + }, { name: "expired-next-update", ocspRes: ocsp.Response{ @@ -374,7 +401,15 @@ func TestUnitBadOCSPResponses(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { ocspHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - response := buildOcspResponse(t, rootCa, rootCaKey, tc.ocspRes) + useCa := rootCa + useCaKey := rootCaKey + if tc.ca != nil { + useCa = tc.ca + } + if tc.caKey != nil { + useCaKey = tc.caKey + } + response := buildOcspResponse(t, useCa, useCaKey, tc.ocspRes) _, _ = w.Write(response) }) ts := httptest.NewServer(ocspHandler) @@ -449,6 +484,51 @@ func TestUnitZeroNextUpdateAreNotCached(t *testing.T) { require.Equal(t, uint32(2), numQueries.Load()) } +// TestUnitResponsesAreCached verify that the OCSP responses are properly cached when +// querying for the same leaf certificates +func TestUnitResponsesAreCached(t *testing.T) { + rootCaKey, rootCa, leafCert := createCaLeafCerts(t) + numQueries := &atomic.Uint32{} + ocspHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + numQueries.Add(1) + now := time.Now() + ocspRes := ocsp.Response{ + SerialNumber: leafCert.SerialNumber, + ThisUpdate: now.Add(-1 * time.Hour), + NextUpdate: now.Add(1 * time.Hour), + Status: ocsp.Good, + } + response := buildOcspResponse(t, rootCa, rootCaKey, ocspRes) + _, _ = w.Write(response) + }) + ts1 := httptest.NewServer(ocspHandler) + ts2 := httptest.NewServer(ocspHandler) + defer ts1.Close() + defer ts2.Close() + + logFactory := func() hclog.Logger { + return hclog.NewNullLogger() + } + client := New(logFactory, 100) + + config := &VerifyConfig{ + OcspEnabled: true, + OcspServersOverride: []string{ts1.URL, ts2.URL}, + QueryAllServers: true, + } + + _, err := client.GetRevocationStatus(context.Background(), leafCert, rootCa, config) + require.NoError(t, err, "Failed fetching revocation status") + // Make sure that we queried both servers and not the cache + require.Equal(t, uint32(2), numQueries.Load()) + + // These query should be cached and not influence our counter + _, err = client.GetRevocationStatus(context.Background(), leafCert, rootCa, config) + require.NoError(t, err, "Failed fetching revocation status second time") + + require.Equal(t, uint32(2), numQueries.Load()) +} + func buildOcspResponse(t *testing.T, ca *x509.Certificate, caKey *ecdsa.PrivateKey, ocspRes ocsp.Response) []byte { response, err := ocsp.CreateResponse(ca, ca, ocspRes, caKey) if err != nil { @@ -598,7 +678,7 @@ func TestOCSPRetry(t *testing.T) { context.TODO(), client, fakeRequestFunc, dummyOCSPHost, - make(map[string]string), []byte{0}, certs[len(certs)-1]) + make(map[string]string), []byte{0}, certs[0], certs[len(certs)-1]) if err == nil { fmt.Printf("should fail: %v, %v, %v\n", res, b, st) } @@ -613,7 +693,7 @@ func TestOCSPRetry(t *testing.T) { context.TODO(), client, fakeRequestFunc, dummyOCSPHost, - make(map[string]string), []byte{0}, certs[len(certs)-1]) + make(map[string]string), []byte{0}, certs[0], certs[len(certs)-1]) if err == nil { fmt.Printf("should fail: %v, %v, %v\n", res, b, st) }