From a4a461466b41f8d912ac3cba9072cbc7dcb8bafa Mon Sep 17 00:00:00 2001 From: max furman Date: Thu, 25 Oct 2018 23:49:23 -0700 Subject: [PATCH] withProvisionerOID and unit test --- authority/authorize.go | 1 - authority/provisioner.go | 1 + authority/tls.go | 38 ++++++++++------- authority/tls_test.go | 91 ++++++++++++++++++++++------------------ 4 files changed, 76 insertions(+), 55 deletions(-) diff --git a/authority/authorize.go b/authority/authorize.go index ebf7e485..b131a95c 100644 --- a/authority/authorize.go +++ b/authority/authorize.go @@ -96,7 +96,6 @@ func (a *Authority) Authorize(ott string) ([]interface{}, error) { &commonNameClaim{claims.Subject}, &dnsNamesClaim{claims.Subject}, &ipAddressesClaim{claims.Subject}, - withIssuerAlternativeNameExtension(p.Issuer + ":" + p.Key.KeyID), p, } diff --git a/authority/provisioner.go b/authority/provisioner.go index 21fa23bc..d1d7a316 100644 --- a/authority/provisioner.go +++ b/authority/provisioner.go @@ -124,6 +124,7 @@ func (p *Provisioner) getTLSApps(so SignOptions) ([]x509util.WithOption, []certC return []x509util.WithOption{ x509util.WithNotBeforeAfterDuration(so.NotBefore, so.NotAfter, c.DefaultTLSCertDuration()), + withProvisionerOID(p.Issuer, p.Key.KeyID), }, []certClaim{ &certTemporalClaim{ min: c.MinTLSCertDuration(), diff --git a/authority/tls.go b/authority/tls.go index 29572edd..da15ed51 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -29,26 +29,36 @@ type SignOptions struct { NotBefore time.Time `json:"notBefore"` } -func withIssuerAlternativeNameExtension(name string) x509util.WithOption { +var ( + stepOIDRoot = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 37476, 9000, 64}) + stepOIDProvisioner = asn1.ObjectIdentifier(append([]int(nil), append(stepOIDRoot, 1)...)) + stepOIDProvisionerName = asn1.ObjectIdentifier(append([]int(nil), append(stepOIDProvisioner, 1)...)) + stepOIDProvisionerKeyID = asn1.ObjectIdentifier(append([]int(nil), append(stepOIDProvisioner, 2)...)) +) + +func withProvisionerOID(name, kid string) x509util.WithOption { return func(p x509util.Profile) error { crt := p.Subject() - iatExt := []asn1.RawValue{ - asn1.RawValue{ - Tag: 2, - Class: 2, - Bytes: []byte(name), - }, - } - iatExtBytes, err := asn1.Marshal(iatExt) - if err != nil { - return &apiError{err, http.StatusInternalServerError, nil} - } + irw := asn1.RawValue{Tag: asn1.TagGeneralString, Class: asn1.ClassPrivate, Bytes: []byte(name)} + krw := asn1.RawValue{Tag: asn1.TagGeneralString, Class: asn1.ClassPrivate, Bytes: []byte(kid)} + irwb, err := asn1.Marshal(irw) + if err != nil { + return err + } + krwb, err := asn1.Marshal(krw) + if err != nil { + return err + } crt.ExtraExtensions = append(crt.ExtraExtensions, pkix.Extension{ - Id: []int{2, 5, 9, 18}, + Id: stepOIDProvisionerName, Critical: false, - Value: iatExtBytes, + Value: irwb, + }, pkix.Extension{ + Id: stepOIDProvisionerKeyID, + Critical: false, + Value: krwb, }) return nil diff --git a/authority/tls_test.go b/authority/tls_test.go index d8ed7382..ef4f8998 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -5,6 +5,7 @@ import ( "crypto/sha1" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "fmt" "net/http" "testing" @@ -50,6 +51,8 @@ func TestSign(t *testing.T) { NotAfter: nb.Add(time.Minute * 5), } + p := a.config.AuthorityConfig.Provisioners[1] + type signTest struct { auth *Authority csr *x509.CertificateRequest @@ -62,13 +65,10 @@ func TestSign(t *testing.T) { csr := getCSR(t, priv) csr.Raw = []byte("foo") return &signTest{ - auth: a, - csr: csr, - extraOpts: []interface{}{ - withIssuerAlternativeNameExtension("baz"), - "42", - }, - signOpts: signOpts, + auth: a, + csr: csr, + extraOpts: []interface{}{p, "42"}, + signOpts: signOpts, err: &apiError{errors.New("sign: invalid extra option type string"), http.StatusInternalServerError, context{"csr": csr, "signOptions": signOpts}, @@ -79,12 +79,10 @@ func TestSign(t *testing.T) { csr := getCSR(t, priv) csr.Raw = []byte("foo") return &signTest{ - auth: a, - csr: csr, - extraOpts: []interface{}{ - withIssuerAlternativeNameExtension("baz"), - }, - signOpts: signOpts, + auth: a, + csr: csr, + extraOpts: []interface{}{p}, + signOpts: signOpts, err: &apiError{errors.New("sign: error converting x509 csr to stepx509 csr"), http.StatusInternalServerError, context{"csr": csr, "signOptions": signOpts}, @@ -96,13 +94,10 @@ func TestSign(t *testing.T) { _a.config.AuthorityConfig.Template = nil csr := getCSR(t, priv) return &signTest{ - auth: _a, - csr: csr, - extraOpts: []interface{}{ - withIssuerAlternativeNameExtension("baz"), - a.config.AuthorityConfig.Provisioners[1], - }, - signOpts: signOpts, + auth: _a, + csr: csr, + extraOpts: []interface{}{p}, + signOpts: signOpts, err: &apiError{errors.New("sign: default ASN1DN template cannot be nil"), http.StatusInternalServerError, context{"csr": csr, "signOptions": signOpts}, @@ -114,12 +109,10 @@ func TestSign(t *testing.T) { _a.intermediateIdentity.Key = nil csr := getCSR(t, priv) return &signTest{ - auth: _a, - csr: csr, - extraOpts: []interface{}{ - withIssuerAlternativeNameExtension("baz"), - }, - signOpts: signOpts, + auth: _a, + csr: csr, + extraOpts: []interface{}{p}, + signOpts: signOpts, err: &apiError{errors.New("sign: error creating new leaf certificate"), http.StatusInternalServerError, context{"csr": csr, "signOptions": signOpts}, @@ -133,13 +126,10 @@ func TestSign(t *testing.T) { NotAfter: nb.Add(time.Hour * 25), } return &signTest{ - auth: a, - csr: csr, - extraOpts: []interface{}{ - withIssuerAlternativeNameExtension("baz"), - a.config.AuthorityConfig.Provisioners[1], - }, - signOpts: _signOpts, + auth: a, + csr: csr, + extraOpts: []interface{}{p}, + signOpts: _signOpts, err: &apiError{errors.New("sign: requested duration of 25h0m0s is more than the authorized maximum certificate duration of 24h0m0s"), http.StatusUnauthorized, context{"csr": csr, "signOptions": _signOpts}, @@ -149,18 +139,18 @@ func TestSign(t *testing.T) { "ok": func(t *testing.T) *signTest { csr := getCSR(t, priv) return &signTest{ - auth: a, - csr: csr, - extraOpts: []interface{}{ - withIssuerAlternativeNameExtension("baz"), - a.config.AuthorityConfig.Provisioners[1], - }, - signOpts: signOpts, + auth: a, + csr: csr, + extraOpts: []interface{}{p}, + signOpts: signOpts, } }, } for name, genTestCase := range tests { + if name != "ok" { + continue + } t.Run(name, func(t *testing.T) { tc := genTestCase(t) @@ -205,6 +195,27 @@ func TestSign(t *testing.T) { assert.Equals(t, leaf.AuthorityKeyId, a.intermediateIdentity.Crt.SubjectKeyId) + // Verify Provisioner OID + found := 0 + for _, ext := range leaf.Extensions { + id := ext.Id.String() + if id != stepOIDProvisionerName.String() && id != stepOIDProvisionerKeyID.String() { + continue + } + found++ + rw := asn1.RawValue{} + _, err := asn1.Unmarshal(ext.Value, &rw) + assert.FatalError(t, err) + assert.Equals(t, rw.Tag, asn1.TagGeneralString) + assert.Equals(t, rw.Class, asn1.ClassPrivate) + if id == stepOIDProvisionerName.String() { + assert.Equals(t, string(rw.Bytes), p.Issuer) + } else { + assert.Equals(t, string(rw.Bytes), p.Key.KeyID) + } + } + assert.Equals(t, found, 2) + realIntermediate, err := x509.ParseCertificate(a.intermediateIdentity.Crt.Raw) assert.FatalError(t, err) assert.Equals(t, intermediate, realIntermediate)