Skip to content

Commit

Permalink
Profiles: allow for omission of KU, EKU, and SKID
Browse files Browse the repository at this point in the history
  • Loading branch information
aarongable committed Jul 25, 2024
1 parent cf8e5aa commit 754758b
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 20 deletions.
57 changes: 42 additions & 15 deletions issuance/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ type ProfileConfig struct {
// OmitCommonName causes the CN field to be excluded from the resulting
// certificate, regardless of its inclusion in the IssuanceRequest.
OmitCommonName bool
// OmitKeyEncipherment causes the keyEncipherment bit to be omitted from the
// Key Usage field of all certificates (instead of only from ECDSA certs).
OmitKeyEncipherment bool
// OmitClientAuth causes the id-kp-clientAuth OID (TLS CLient Authentication)
// to be omitted from the EKU extension.
OmitClientAuth bool
// OmitSKID causes the Subject Key Identifier extension to be omitted.
OmitSKID bool

MaxValidityPeriod config.Duration
MaxValidityBackdate config.Duration
Expand All @@ -61,8 +69,11 @@ type PolicyConfig struct {

// Profile is the validated structure created by reading in ProfileConfigs and IssuerConfigs
type Profile struct {
allowMustStaple bool
omitCommonName bool
allowMustStaple bool
omitCommonName bool
omitKeyEncipherment bool
omitClientAuth bool
omitSKID bool

maxBackdate time.Duration
maxValidity time.Duration
Expand All @@ -85,11 +96,14 @@ func NewProfile(profileConfig ProfileConfig, lints lint.Registry) (*Profile, err
}

sp := &Profile{
allowMustStaple: profileConfig.AllowMustStaple,
omitCommonName: profileConfig.OmitCommonName,
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
maxValidity: profileConfig.MaxValidityPeriod.Duration,
lints: lints,
allowMustStaple: profileConfig.AllowMustStaple,
omitCommonName: profileConfig.OmitCommonName,
omitKeyEncipherment: profileConfig.OmitKeyEncipherment,
omitClientAuth: profileConfig.OmitClientAuth,
omitSKID: profileConfig.OmitSKID,
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
maxValidity: profileConfig.MaxValidityPeriod.Duration,
lints: lints,
}

return sp, nil
Expand Down Expand Up @@ -121,7 +135,7 @@ func (i *Issuer) requestValid(clk clock.Clock, prof *Profile, req *IssuanceReque
return errors.New("inactive issuer cannot issue precert")
}

if len(req.SubjectKeyId) != 20 {
if len(req.SubjectKeyId) != 0 && len(req.SubjectKeyId) != 20 {
return errors.New("unexpected subject key ID length")
}

Expand Down Expand Up @@ -162,11 +176,7 @@ func (i *Issuer) requestValid(clk clock.Clock, prof *Profile, req *IssuanceReque

func (i *Issuer) generateTemplate() *x509.Certificate {
template := &x509.Certificate{
SignatureAlgorithm: i.sigAlg,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
},
SignatureAlgorithm: i.sigAlg,
OCSPServer: []string{i.ocspURL},
IssuingCertificateURL: []string{i.issuerURL},
BasicConstraintsValid: true,
Expand Down Expand Up @@ -278,6 +288,17 @@ func (i *Issuer) Prepare(prof *Profile, req *IssuanceRequest) ([]byte, *issuance
// generate template from the issuer's data
template := i.generateTemplate()

ekus := []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
}
if prof.omitClientAuth {
ekus = []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
}
}
template.ExtKeyUsage = ekus

// populate template from the issuance request
template.NotBefore, template.NotAfter = req.NotBefore, req.NotAfter
template.SerialNumber = big.NewInt(0).SetBytes(req.Serial)
Expand All @@ -288,12 +309,18 @@ func (i *Issuer) Prepare(prof *Profile, req *IssuanceRequest) ([]byte, *issuance

switch req.PublicKey.(type) {
case *rsa.PublicKey:
template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
if prof.omitKeyEncipherment {
template.KeyUsage = x509.KeyUsageDigitalSignature
} else {
template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
}
case *ecdsa.PublicKey:
template.KeyUsage = x509.KeyUsageDigitalSignature
}

template.SubjectKeyId = req.SubjectKeyId
if !prof.omitSKID {
template.SubjectKeyId = req.SubjectKeyId
}

if req.IncludeCTPoison {
template.ExtraExtensions = append(template.ExtraExtensions, ctPoisonExt)
Expand Down
50 changes: 45 additions & 5 deletions issuance/cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,6 @@ func TestGenerateTemplate(t *testing.T) {
expected := &x509.Certificate{
BasicConstraintsValid: true,
SignatureAlgorithm: x509.SHA256WithRSA,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
},
IssuingCertificateURL: []string{"http://issuer"},
OCSPServer: []string{"http://ocsp"},
CRLDistributionPoints: nil,
Expand Down Expand Up @@ -448,11 +444,55 @@ func TestIssueCommonName(t *testing.T) {
test.AssertEquals(t, cert.Subject.CommonName, "")
}

func TestIssueCTPoison(t *testing.T) {
func TestIssueOmissions(t *testing.T) {
fc := clock.NewFake()
fc.Set(time.Now())

lints, err := linter.NewRegistry([]string{
"w_ext_subject_key_identifier_missing_sub_cert",
"w_ct_sct_policy_count_unsatisfied",
"e_scts_from_same_operator",
})
test.AssertNotError(t, err, "building test lint registry")
pc := defaultProfileConfig()
pc.OmitCommonName = true
pc.OmitKeyEncipherment = true
pc.OmitClientAuth = true
pc.OmitSKID = true
prof, err := NewProfile(pc, lints)
test.AssertNotError(t, err, "building test profile")

signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
test.AssertNotError(t, err, "NewIssuer failed")

pk, err := rsa.GenerateKey(rand.Reader, 2048)
test.AssertNotError(t, err, "failed to generate test key")
_, issuanceToken, err := signer.Prepare(prof, &IssuanceRequest{
PublicKey: pk.Public(),
SubjectKeyId: goodSKID,
Serial: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
DNSNames: []string{"example.com"},
CommonName: "example.com",
IncludeCTPoison: true,
NotBefore: fc.Now(),
NotAfter: fc.Now().Add(time.Hour - time.Second),
})
test.AssertNotError(t, err, "Prepare failed")
certBytes, err := signer.Issue(issuanceToken)
test.AssertNotError(t, err, "Issue failed")
cert, err := x509.ParseCertificate(certBytes)
test.AssertNotError(t, err, "failed to parse certificate")

test.AssertEquals(t, cert.Subject.CommonName, "")
test.AssertEquals(t, cert.KeyUsage, x509.KeyUsageDigitalSignature)
test.AssertDeepEquals(t, cert.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
test.AssertEquals(t, len(cert.SubjectKeyId), 0)
}

func TestIssueCTPoison(t *testing.T) {
fc := clock.NewFake()
fc.Set(time.Now())
signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
test.AssertNotError(t, err, "NewIssuer failed")
pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
test.AssertNotError(t, err, "failed to generate test key")
Expand Down
3 changes: 3 additions & 0 deletions test/config-next/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
"modern": {
"allowMustStaple": true,
"omitCommonName": true,
"omitKeyEncipherment": true,
"omitClientAuth": true,
"omitSKID": true,
"policies": [
{
"oid": "2.23.140.1.2.1"
Expand Down

0 comments on commit 754758b

Please sign in to comment.