CA: Set certificates' validity periods using the CA's clock. (#2983)

This commit is contained in:
Brian Smith 2017-09-14 03:40:31 -10:00 committed by Daniel McCarney
parent 1b156822a1
commit 9d324631a7
4 changed files with 44 additions and 17 deletions

View File

@ -116,6 +116,7 @@ type CertificateAuthorityImpl struct {
stats metrics.Scope
prefix int // Prepended to the serial number
validityPeriod time.Duration
backdate time.Duration
maxNames int
forceCNFromSAN bool
enableMustStaple bool
@ -270,6 +271,14 @@ func NewCertificateAuthorityImpl(
return nil, err
}
// TODO(briansmith): Make the backdate setting mandatory after the
// production ca.json has been updated to include it. Until then, manually
// default to 1h, which is the backdating duration we currently use.
ca.backdate = config.Backdate.Duration
if ca.backdate == 0 {
ca.backdate = time.Hour
}
ca.maxNames = config.MaxNames
return ca, nil
@ -404,12 +413,12 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(ctx context.Context, issueR
return emptyCert, berrors.InternalServerError("RegistrationID is nil")
}
notAfter, serialBigInt, err := ca.generateNotAfterAndSerialNumber()
serialBigInt, validity, err := ca.generateSerialNumberAndValidity()
if err != nil {
return emptyCert, err
}
certDER, err := ca.issueCertificateOrPrecertificate(ctx, issueReq, notAfter, serialBigInt, "cert", nil)
certDER, err := ca.issueCertificateOrPrecertificate(ctx, issueReq, serialBigInt, validity, "cert", nil)
if err != nil {
return emptyCert, err
}
@ -426,12 +435,12 @@ func (ca *CertificateAuthorityImpl) IssuePrecertificate(ctx context.Context, iss
return nil, berrors.InternalServerError("RegistrationID is nil")
}
notAfter, serialBigInt, err := ca.generateNotAfterAndSerialNumber()
serialBigInt, validity, err := ca.generateSerialNumberAndValidity()
if err != nil {
return nil, err
}
precertDER, err := ca.issueCertificateOrPrecertificate(ctx, issueReq, notAfter, serialBigInt, "cert", &ctPoisonExtension)
precertDER, err := ca.issueCertificateOrPrecertificate(ctx, issueReq, serialBigInt, validity, "cert", &ctPoisonExtension)
if err != nil {
return nil, err
}
@ -447,9 +456,12 @@ func (ca *CertificateAuthorityImpl) IssueCertificateForPrecertificate(ctx contex
return emptyCert, berrors.InternalServerError("IssueCertificateForPrecertificate is not implemented")
}
func (ca *CertificateAuthorityImpl) generateNotAfterAndSerialNumber() (time.Time, *big.Int, error) {
notAfter := ca.clk.Now().Add(ca.validityPeriod)
type validity struct {
NotBefore time.Time
NotAfter time.Time
}
func (ca *CertificateAuthorityImpl) generateSerialNumberAndValidity() (*big.Int, validity, error) {
// We want 136 bits of random number, plus an 8-bit instance id prefix.
const randBits = 136
serialBytes := make([]byte, randBits/8+1)
@ -458,15 +470,21 @@ func (ca *CertificateAuthorityImpl) generateNotAfterAndSerialNumber() (time.Time
if err != nil {
err = berrors.InternalServerError("failed to generate serial: %s", err)
ca.log.AuditErr(fmt.Sprintf("Serial randomness failed, err=[%v]", err))
return time.Time{}, nil, err
return nil, validity{}, err
}
serialBigInt := big.NewInt(0)
serialBigInt = serialBigInt.SetBytes(serialBytes)
return notAfter, serialBigInt, nil
notBefore := ca.clk.Now().Add(-1 * ca.backdate)
validity := validity{
NotBefore: notBefore,
NotAfter: notBefore.Add(ca.validityPeriod),
}
return serialBigInt, validity, nil
}
func (ca *CertificateAuthorityImpl) issueCertificateOrPrecertificate(ctx context.Context, issueReq *caPB.IssueCertificateRequest, notAfter time.Time, serialBigInt *big.Int, certType string, addedExtension *signer.Extension) ([]byte, error) {
func (ca *CertificateAuthorityImpl) issueCertificateOrPrecertificate(ctx context.Context, issueReq *caPB.IssueCertificateRequest, serialBigInt *big.Int, validity validity, certType string, addedExtension *signer.Extension) ([]byte, error) {
csr, err := x509.ParseCertificateRequest(issueReq.Csr)
if err != nil {
return nil, err
@ -495,7 +513,7 @@ func (ca *CertificateAuthorityImpl) issueCertificateOrPrecertificate(ctx context
issuer := ca.defaultIssuer
if issuer.cert.NotAfter.Before(notAfter) {
if issuer.cert.NotAfter.Before(validity.NotAfter) {
err = berrors.InternalServerError("cannot issue a certificate that expires after the issuer certificate")
ca.log.AuditErr(err.Error())
return nil, err
@ -529,6 +547,8 @@ func (ca *CertificateAuthorityImpl) issueCertificateOrPrecertificate(ctx context
},
Serial: serialBigInt,
Extensions: extensions,
NotBefore: validity.NotBefore,
NotAfter: validity.NotAfter,
}
serialHex := core.SerialToString(serialBigInt)

View File

@ -202,6 +202,10 @@ func setup(t *testing.T) *testCtx {
ECDSAProfile: ecdsaProfileName,
SerialPrefix: 17,
Expiry: "8760h",
// TODO(briansmith): When the defaulting of Backdate is removed, this
// will need to be uncommented. Leave it commented for now to test the
// defaulting logic.
// Backdate: cmd.ConfigDuration{Duration: time.Hour},
LifespanOCSP: cmd.ConfigDuration{Duration: 45 * time.Minute},
MaxNames: 2,
CFSSL: cfsslConfig.Config{
@ -318,6 +322,7 @@ func TestIssueCertificate(t *testing.T) {
subTest func(t *testing.T, i *TestCertificateIssuance)
}{
{"IssueCertificate", CNandSANCSR, issueCertificateSubTestDefaultSetup, issueCertificateSubTestIssueCertificate},
{"ValidityUsesCAClock", CNandSANCSR, issueCertificateSubTestDefaultSetup, issueCertificateSubTestValidityUsesCAClock},
{"AllowNoCN", NoCNCSR, issueCertificateSubTestDefaultSetup, issueCertificateSubTestAllowNoCN},
{"ProfileSelectionRSA", CNandSANCSR, issueCertificateSubTestDefaultSetup, issueCertificateSubTestProfileSelectionRSA},
{"ProfileSelectionECDSA", ECDSACSR, issueCertificateSubTestDefaultSetup, issueCertificateSubTestProfileSelectionECDSA},
@ -407,13 +412,6 @@ func TestIssueCertificate(t *testing.T) {
func issueCertificateSubTestDefaultSetup(t *testing.T) (*CertificateAuthorityImpl, *mockSA) {
testCtx := setup(t)
// Although the CA generally uses its own clock (ca.clk) to generate
// timestamps, the notBefore date is set based on the current system time.
// That's wrong, but work around it for now by syncing the fake clock with
// the system clock.
testCtx.fc.Set(clock.New().Now())
sa := &mockSA{}
ca, err := NewCertificateAuthorityImpl(
testCtx.caConfig,
@ -453,6 +451,11 @@ func issueCertificateSubTestIssueCertificate(t *testing.T, i *TestCertificateIss
}
}
func issueCertificateSubTestValidityUsesCAClock(t *testing.T, i *TestCertificateIssuance) {
test.AssertEquals(t, i.cert.NotBefore, i.ca.clk.Now().Add(-1*i.ca.backdate))
test.AssertEquals(t, i.cert.NotAfter, i.cert.NotBefore.Add(i.ca.validityPeriod))
}
// Test issuing when multiple issuers are present.
func TestMultipleIssuers(t *testing.T) {
testCtx := setup(t)

View File

@ -99,6 +99,9 @@ type CAConfig struct {
// How long issued certificates are valid for, should match expiry field
// in cfssl config.
Expiry string
// How far back certificates should be backdated, should match backdate
// field in cfssl config.
Backdate ConfigDuration
// The maximum number of subjectAltNames in a single certificate
MaxNames int
CFSSL cfsslConfig.Config

View File

@ -36,6 +36,7 @@
"NumSessions": 2
}],
"expiry": "2160h",
"backdate": "1h",
"lifespanOCSP": "96h",
"maxNames": 100,
"doNotForceCN": true,