Skip to content

Commit a8591c3

Browse files
author
Watson Ladd
committed
Support for DelegationUsage extension
This special cases the DelegationUsage extension, copying it to the output when signing. It also adds support for a flag delegation_enabled in certificate specifications. I have manually confirmed this works.
1 parent 29ae05f commit a8591c3

5 files changed

Lines changed: 81 additions & 15 deletions

File tree

cli/testdata/csr.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818
"1.2.3.4.5": "abc"
1919
}
2020
}
21-
]
22-
}
21+
],
22+
"delegation_enabled": true
23+
}

csr/csr.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type Name struct {
3737
L string `json:"L,omitempty" yaml:"L,omitempty"` // Locality
3838
O string `json:"O,omitempty" yaml:"O,omitempty"` // OrganisationName
3939
OU string `json:"OU,omitempty" yaml:"OU,omitempty"` // OrganisationalUnitName
40-
E string `json:"E,omitempty" yaml:"E,omitempty"`
40+
E string `json:"E,omitempty" yaml:"E,omitempty"`
4141
SerialNumber string `json:"SerialNumber,omitempty" yaml:"SerialNumber,omitempty"`
4242
OID map[string]string `json:"OID,omitempty", yaml:"OID,omitempty"`
4343
}
@@ -136,14 +136,15 @@ type CAConfig struct {
136136
// A CertificateRequest encapsulates the API interface to the
137137
// certificate request functionality.
138138
type CertificateRequest struct {
139-
CN string `json:"CN" yaml:"CN"`
140-
Names []Name `json:"names" yaml:"names"`
141-
Hosts []string `json:"hosts" yaml:"hosts"`
142-
KeyRequest *KeyRequest `json:"key,omitempty" yaml:"key,omitempty"`
143-
CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"`
144-
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
145-
Extensions []pkix.Extension `json:"extensions,omitempty" yaml:"extensions,omitempty"`
146-
CRL string `json:"crl_url,omitempty" yaml:"crl_url,omitempty"`
139+
CN string `json:"CN" yaml:"CN"`
140+
Names []Name `json:"names" yaml:"names"`
141+
Hosts []string `json:"hosts" yaml:"hosts"`
142+
KeyRequest *KeyRequest `json:"key,omitempty" yaml:"key,omitempty"`
143+
CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"`
144+
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
145+
DelegationEnabled bool `json:"delegation_enabled,omitempty" yaml:"delegation_enabled,omitempty"`
146+
Extensions []pkix.Extension `json:"extensions,omitempty" yaml:"extensions,omitempty"`
147+
CRL string `json:"crl_url,omitempty" yaml:"crl_url,omitempty"`
147148
}
148149

149150
// New returns a new, empty CertificateRequest with a
@@ -196,9 +197,9 @@ func (cr *CertificateRequest) Name() (pkix.Name, error) {
196197
}
197198
name.ExtraNames = append(name.ExtraNames, pkix.AttributeTypeAndValue{Type: oid, Value: v})
198199
}
199-
if n.E != "" {
200-
name.ExtraNames = append(name.ExtraNames, pkix.AttributeTypeAndValue{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: n.E})
201-
}
200+
if n.E != "" {
201+
name.ExtraNames = append(name.ExtraNames, pkix.AttributeTypeAndValue{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: n.E})
202+
}
202203
}
203204
name.SerialNumber = cr.SerialNumber
204205
return name, nil
@@ -430,6 +431,10 @@ func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err erro
430431
}
431432
}
432433

434+
if req.DelegationEnabled {
435+
tpl.ExtraExtensions = append(tpl.Extensions, helpers.DelegationExtension)
436+
}
437+
433438
if req.Extensions != nil {
434439
err = appendExtensionsToCSR(req.Extensions, &tpl)
435440
if err != nil {

csr/csr_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,3 +786,50 @@ func TestExtractCertificateRequest(t *testing.T) {
786786
t.Fatal("Bad Certificate Request!")
787787
}
788788
}
789+
790+
// TestDelegationCSR tests that we create requests with the DC extension
791+
func TestDelegationCSR(t *testing.T) {
792+
var cr = &CertificateRequest{
793+
CN: "Test Common Name",
794+
Names: []Name{
795+
{
796+
C: "US",
797+
ST: "California",
798+
L: "San Francisco",
799+
O: "CloudFlare, Inc.",
800+
OU: "Systems Engineering",
801+
},
802+
{
803+
C: "GB",
804+
ST: "London",
805+
L: "London",
806+
O: "CloudFlare, Inc",
807+
OU: "Systems Engineering",
808+
},
809+
},
810+
DelegationEnabled: true,
811+
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
812+
KeyRequest: NewKeyRequest(),
813+
}
814+
csr, _, err := ParseRequest(cr)
815+
if err != nil {
816+
t.Fatal("could not generate csr")
817+
}
818+
unPem, _ := pem.Decode(csr)
819+
if unPem == nil {
820+
t.Fatal("Failed to decode pem")
821+
}
822+
res, err := x509.ParseCertificateRequest(unPem.Bytes)
823+
if err != nil {
824+
t.Fatalf("spat out nonsense as a csr: %v", err)
825+
}
826+
found := false
827+
for _, ext := range res.Extensions {
828+
if ext.Id.Equal(helpers.DelegationUsage) {
829+
found = true
830+
}
831+
}
832+
if !found {
833+
t.Fatal("generated csr has no extension")
834+
}
835+
}

helpers/helpers.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ const OneYear = 8760 * time.Hour
3939
// OneDay is a time.Duration representing a day's worth of seconds.
4040
const OneDay = 24 * time.Hour
4141

42+
// DelegationUsage is the OID for the DelegationUseage extensions
43+
var DelegationUsage = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 44}
44+
45+
// DelegationExtension
46+
var DelegationExtension = pkix.Extension{
47+
Id: DelegationUsage,
48+
Critical: false,
49+
Value: []byte{0x05, 0x00}, // ASN.1 NULL
50+
}
51+
4252
// InclusiveDate returns the time.Time representation of a date - 1
4353
// nanosecond. This allows time.After to be used inclusively.
4454
func InclusiveDate(year int, month time.Month, day int) time.Time {

signer/signer.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/cloudflare/cfssl/config"
2121
"github.com/cloudflare/cfssl/csr"
2222
cferr "github.com/cloudflare/cfssl/errors"
23+
"github.com/cloudflare/cfssl/helpers"
2324
"github.com/cloudflare/cfssl/info"
2425
)
2526

@@ -45,7 +46,7 @@ type Extension struct {
4546
// Extensions provided in the signRequest are copied into the certificate, as
4647
// long as they are in the ExtensionWhitelist for the signer's policy.
4748
// Extensions requested in the CSR are ignored, except for those processed by
48-
// ParseCertificateRequest (mainly subjectAltName).
49+
// ParseCertificateRequest (mainly subjectAltName) and DelegationUsage.
4950
type SignRequest struct {
5051
Hosts []string `json:"hosts"`
5152
Request string `json:"certificate_request"`
@@ -240,6 +241,8 @@ func ParseCertificateRequest(s Signer, p *config.SigningProfile, csrBytes []byte
240241
template.IsCA = constraints.IsCA
241242
template.MaxPathLen = constraints.MaxPathLen
242243
template.MaxPathLenZero = template.MaxPathLen == 0
244+
} else if val.Id.Equal(helpers.DelegationUsage) {
245+
template.ExtraExtensions = append(template.ExtraExtensions, val)
243246
} else {
244247
// If the profile has 'copy_extensions' to true then lets add it
245248
if p.CopyExtensions {

0 commit comments

Comments
 (0)