cmd/single-ocsp: validate issuer/responder are valid to sign the response (#4855)

Fixes #4853
This commit is contained in:
Roland Bracewell Shoemaker 2020-06-11 17:11:29 -07:00 committed by GitHub
parent 0fcfbc3e16
commit 7f3c0f61a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 106 additions and 3 deletions

View File

@ -4,6 +4,7 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
@ -84,6 +85,33 @@ func readFiles(issuerFileName, responderFileName, targetFileName, pkcs11FileName
return
}
func signerValidForResp(issuer, responder *x509.Certificate, thisUpdate, nextUpdate time.Time) error {
if !issuer.Equal(responder) {
if err := responder.CheckSignatureFrom(issuer); err != nil {
return fmt.Errorf("invalid signature on responder from issuer: %s", err)
}
gotOCSPEKU := false
for _, eku := range responder.ExtKeyUsage {
if eku == x509.ExtKeyUsageOCSPSigning {
gotOCSPEKU = true
break
}
}
if !gotOCSPEKU {
return errors.New("responder certificate doesn't contain OCSPSigning extended key usage")
}
}
if thisUpdate.Before(responder.NotBefore) {
return errors.New("thisUpdate is before responder certificate's notBefore")
} else if nextUpdate.After(responder.NotAfter) {
return errors.New("nextUpdate is after responder certificate's notAfter")
}
return nil
}
func main() {
issuerFile := flag.String("issuer", "", "Issuer certificate (PEM)")
responderFile := flag.String("responder", "", "OCSP responder certificate (PEM)")
@ -107,9 +135,16 @@ func main() {
nextUpdate, err := time.Parse(time.RFC3339, *nextUpdateString)
cmd.FailOnError(err, "Parsing nextUpdate flag")
if nextUpdate.Before(thisUpdate) {
cmd.Fail("nextUpdate must be after thisUpdate")
}
issuer, responder, target, pkcs11, err := readFiles(*issuerFile, *responderFile, *targetFile, *pkcs11File)
cmd.FailOnError(err, "Failed to read files")
err = signerValidForResp(issuer, responder, thisUpdate, nextUpdate)
cmd.FailOnError(err, "Issuer/Responder are not valid for response")
// Instantiate the private key from PKCS11
priv, err := pkcs11key.New(pkcs11.Module, pkcs11.TokenLabel, pkcs11.PIN, responder.PublicKey)
cmd.FailOnError(err, "Failed to load PKCS#11 key")

View File

@ -1 +1,66 @@
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"testing"
"time"
"github.com/letsencrypt/boulder/test"
)
func TestSignerValidForResp(t *testing.T) {
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
test.AssertNotError(t, err, "ecdsa.GenerateKey failed")
now := time.Now()
template := &x509.Certificate{
Subject: pkix.Name{CommonName: "test"},
SerialNumber: big.NewInt(1),
NotBefore: now,
NotAfter: now.Add(time.Hour * 3),
}
issuerDER, err := x509.CreateCertificate(rand.Reader, template, template, k.Public(), k)
test.AssertNotError(t, err, "failed to create issuer cert")
issuer, err := x509.ParseCertificate(issuerDER)
test.AssertNotError(t, err, "failed to parse issuer cert")
responderDER, err := x509.CreateCertificate(rand.Reader, template, issuer, k.Public(), k)
test.AssertNotError(t, err, "failed to create responder cert")
responder, err := x509.ParseCertificate(responderDER)
test.AssertNotError(t, err, "failed to parse responder cert")
err = signerValidForResp(issuer, responder, time.Time{}, time.Time{})
test.AssertError(t, err, "signerValidForResp didn't fail")
test.AssertEquals(t, err.Error(), "invalid signature on responder from issuer: x509: invalid signature: parent certificate cannot sign this kind of certificate")
template.IsCA, template.BasicConstraintsValid = true, true
issuerDER, err = x509.CreateCertificate(rand.Reader, template, template, k.Public(), k)
test.AssertNotError(t, err, "failed to create issuer cert")
issuer, err = x509.ParseCertificate(issuerDER)
test.AssertNotError(t, err, "failed to parse issuer cert")
err = signerValidForResp(issuer, responder, time.Time{}, time.Time{})
test.AssertError(t, err, "signerValidForResp didn't fail")
test.AssertEquals(t, err.Error(), "responder certificate doesn't contain OCSPSigning extended key usage")
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}
responderDER, err = x509.CreateCertificate(rand.Reader, template, issuer, k.Public(), k)
test.AssertNotError(t, err, "failed to create responder cert")
responder, err = x509.ParseCertificate(responderDER)
test.AssertNotError(t, err, "failed to parse responder cert")
err = signerValidForResp(issuer, responder, time.Time{}, time.Time{})
test.AssertError(t, err, "signerValidForResp didn't fail")
test.AssertEquals(t, err.Error(), "thisUpdate is before responder certificate's notBefore")
err = signerValidForResp(issuer, responder, now.Add(time.Hour), now.Add(time.Hour*4))
test.AssertError(t, err, "signerValidForResp didn't fail")
test.AssertEquals(t, err.Error(), "nextUpdate is after responder certificate's notAfter")
err = signerValidForResp(issuer, responder, now.Add(time.Hour), now.Add(time.Hour*2))
test.AssertNotError(t, err, "signerValidForResp failed")
}

View File

@ -191,14 +191,17 @@ def test_single_ocsp():
This is a non-API test.
"""
dateFormat = "%Y-%m-%dT00:00:00Z"
thisUpdate = datetime.datetime.now()
nextUpdate = thisUpdate + datetime.timedelta(weeks=52)
run("./bin/single-ocsp -issuer /tmp/root-cert-rsa.pem \
-responder /tmp/root-cert-rsa.pem \
-target /tmp/intermediate-cert-rsa-a.pem \
-pkcs11 test/test-root.key-pkcs11.json \
-thisUpdate 2016-09-02T00:00:00Z \
-nextUpdate 2020-09-02T00:00:00Z \
-thisUpdate %s \
-nextUpdate %s \
-status 0 \
-out /tmp/issuer-ocsp-responses.txt")
-out /tmp/issuer-ocsp-responses.txt" % (thisUpdate.strftime(dateFormat), nextUpdate.strftime(dateFormat)))
p = subprocess.Popen(
'./bin/ocsp-responder --config test/issuer-ocsp-responder.json', shell=True)