CA: Set certificates' validity periods using the CA's clock. (#2983)
This commit is contained in:
parent
1b156822a1
commit
9d324631a7
40
ca/ca.go
40
ca/ca.go
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"NumSessions": 2
|
||||
}],
|
||||
"expiry": "2160h",
|
||||
"backdate": "1h",
|
||||
"lifespanOCSP": "96h",
|
||||
"maxNames": 100,
|
||||
"doNotForceCN": true,
|
||||
|
|
Loading…
Reference in New Issue