cmd/single-ocsp: validate issuer/responder are valid to sign the response (#4855)
Fixes #4853
This commit is contained in:
parent
0fcfbc3e16
commit
7f3c0f61a3
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue