diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index 55fc52b90014..f4e25235a32e 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -62,6 +62,7 @@ type creationParameters struct { NotAfter time.Time KeyUsage x509.KeyUsage ExtKeyUsage certExtKeyUsage + ExtKeyUsageOIDs []string PolicyIdentifiers []string BasicConstraintsValidForNonCA bool @@ -918,6 +919,7 @@ func generateCreationBundle(b *backend, data *dataBundle) error { NotAfter: notAfter, KeyUsage: x509.KeyUsage(parseKeyUsages(data.role.KeyUsage)), ExtKeyUsage: extUsage, + ExtKeyUsageOIDs: data.role.ExtKeyUsageOIDs, PolicyIdentifiers: data.role.PolicyIdentifiers, BasicConstraintsValidForNonCA: data.role.BasicConstraintsValidForNonCA, } @@ -989,6 +991,16 @@ func addPolicyIdentifiers(data *dataBundle, certTemplate *x509.Certificate) { } } +// addExtKeyUsageOids adds custom extended key usage OIDs to certificate +func addExtKeyUsageOids(data *dataBundle, certTemplate *x509.Certificate) { + for _, oidstr := range data.params.ExtKeyUsageOIDs { + oid, err := stringToOid(oidstr) + if err == nil { + certTemplate.UnknownExtKeyUsage = append(certTemplate.UnknownExtKeyUsage, oid) + } + } +} + // Performs the heavy lifting of creating a certificate. Returns // a fully-filled-in ParsedCertBundle. func createCertificate(data *dataBundle) (*certutil.ParsedCertBundle, error) { @@ -1045,6 +1057,8 @@ func createCertificate(data *dataBundle) (*certutil.ParsedCertBundle, error) { addKeyUsages(data, certTemplate) + addExtKeyUsageOids(data, certTemplate) + certTemplate.IssuingCertificateURL = data.params.URLs.IssuingCertificates certTemplate.CRLDistributionPoints = data.params.URLs.CRLDistributionPoints certTemplate.OCSPServer = data.params.URLs.OCSPServers @@ -1254,6 +1268,8 @@ func signCertificate(data *dataBundle) (*certutil.ParsedCertBundle, error) { addKeyUsages(data, certTemplate) + addExtKeyUsageOids(data, certTemplate) + var certBytes []byte certTemplate.IssuingCertificateURL = data.params.URLs.IssuingCertificates diff --git a/builtin/logical/pki/path_roles.go b/builtin/logical/pki/path_roles.go index c2b706598ffc..e9baf731847f 100644 --- a/builtin/logical/pki/path_roles.go +++ b/builtin/logical/pki/path_roles.go @@ -166,6 +166,11 @@ To remove all key usages from being set, set this value to an empty list.`, }, + "ext_key_usage_oids": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: `A comma-separated string or list of extended key usage oids.`, + }, + "use_csr_common_name": &framework.FieldSchema{ Type: framework.TypeBool, Default: true, @@ -451,6 +456,7 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data UseCSRCommonName: data.Get("use_csr_common_name").(bool), UseCSRSANs: data.Get("use_csr_sans").(bool), KeyUsage: data.Get("key_usage").([]string), + ExtKeyUsageOIDs: data.Get("ext_key_usage_oids").([]string), OU: data.Get("ou").([]string), Organization: data.Get("organization").([]string), Country: data.Get("country").([]string), @@ -495,6 +501,15 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data return errResp, nil } + if len(entry.ExtKeyUsageOIDs) > 0 { + for _, oidstr := range entry.ExtKeyUsageOIDs { + _, err := stringToOid(oidstr) + if err != nil { + return logical.ErrorResponse(fmt.Sprintf("%q could not be parsed as a valid oid for an extended key usage", oidstr)), nil + } + } + } + if len(entry.PolicyIdentifiers) > 0 { for _, oidstr := range entry.PolicyIdentifiers { _, err := stringToOid(oidstr) @@ -588,6 +603,7 @@ type roleEntry struct { RequireCN bool `json:"require_cn" mapstructure:"require_cn"` AllowedOtherSANs []string `json:"allowed_other_sans" mapstructure:"allowed_other_sans"` PolicyIdentifiers []string `json:"policy_identifiers" mapstructure:"policy_identifiers"` + ExtKeyUsageOIDs []string `json:"ext_key_usage_oids" mapstructure:"ext_key_usage_oids"` BasicConstraintsValidForNonCA bool `json:"basic_constraints_valid_for_non_ca" mapstructure:"basic_constraints_valid_for_non_ca"` // Used internally for signing intermediates @@ -616,6 +632,7 @@ func (r *roleEntry) ToResponseData() map[string]interface{} { "key_type": r.KeyType, "key_bits": r.KeyBits, "key_usage": r.KeyUsage, + "ext_key_usage_oids": r.ExtKeyUsageOIDs, "ou": r.OU, "organization": r.Organization, "country": r.Country, diff --git a/ui/app/models/role-pki.js b/ui/app/models/role-pki.js index 9b7dd34480c5..a06d690abe16 100644 --- a/ui/app/models/role-pki.js +++ b/ui/app/models/role-pki.js @@ -55,6 +55,10 @@ export default DS.Model.extend({ defaultValue: 'DigitalSignature,KeyAgreement,KeyEncipherment', editType: 'stringArray', }), + extKeyUsageOids: attr({ + label: 'Custom extended key usage OIDs', + editType: 'stringArray', + }), requireCn: attr('boolean', { label: 'Require common name', defaultValue: true, @@ -223,7 +227,7 @@ export default DS.Model.extend({ 'allowedDomains', ], }, - { 'Extended Key Usage': ['serverFlag', 'clientFlag', 'codeSigningFlag', 'emailProtectionFlag'] }, + { 'Extended Key Usage': ['serverFlag', 'clientFlag', 'codeSigningFlag', 'emailProtectionFlag', 'extKeyUsageOids'] }, { Advanced: ['generateLease', 'noStore', 'basicConstraintsValidForNonCA', 'policyIdentifiers'], },