Key Usage Enablement for Ent-Feature CMPv2 (#28237)

* Key Usage Enablement for Ent-Feature CMPv2
This commit is contained in:
Kit Haines
2024-08-30 13:05:20 -04:00
committed by GitHub
parent 9ba5437ab5
commit edf6851eb4
9 changed files with 118 additions and 16 deletions

View File

@@ -72,6 +72,7 @@ func getGenerationParams(sc *storageContext, data *framework.FieldData) (exporte
PostalCode: data.Get("postal_code").([]string),
NotBeforeDuration: time.Duration(data.Get("not_before_duration").(int)) * time.Second,
CNValidations: []string{"disabled"},
KeyUsage: data.Get("key_usage").([]string),
}
*role.AllowWildcardCertificates = true

View File

@@ -410,6 +410,10 @@ func generateIntermediateCSR(sc *storageContext, input *inputBundle, randomSourc
return nil, nil, errutil.InternalError{Err: "nil parameters received from parameter bundle generation"}
}
_, exists := input.apiData.GetOk("key_usage")
if !exists {
creation.Params.KeyUsage = 0
}
addBasicConstraints := input.apiData != nil && input.apiData.Get("add_basic_constraints").(bool)
parsedBundle, err := generateCSRBundle(sc, input, creation, addBasicConstraints, randomSource)
if err != nil {

View File

@@ -275,6 +275,20 @@ this value.`,
},
}
fields["key_usage"] = &framework.FieldSchema{ // Same Name as Leaf-Cert Field, but Description and Default Differ
Type: framework.TypeCommaStringSlice,
Default: []string{"CertSign", "CRLSign"},
Description: `A comma-separated string or list of key usages (not extended
key usages). Valid values can be found at
https://golang.org/pkg/crypto/x509/#KeyUsage
-- simply drop the "KeyUsage" part of the name.
To remove all key usages from being set, set
this value to an empty list. This defaults to
CertSign, CRLSign for CAs. If neither of those
two set, a warning will be thrown. To use the
issuer for CMPv2, DigitalSignature must be set.`,
} // TODO: Fix Description Here
fields["serial_number"] = &framework.FieldSchema{
Type: framework.TypeString,
Description: `The Subject's requested serial number, if any.

View File

@@ -356,6 +356,7 @@ func (b *backend) pathIssuerSignIntermediate(ctx context.Context, req *logical.R
NotAfter: data.Get("not_after").(string),
NotBeforeDuration: time.Duration(data.Get("not_before_duration").(int)) * time.Second,
CNValidations: []string{"disabled"},
KeyUsage: data.Get("key_usage").([]string),
}
*role.AllowWildcardCertificates = true

View File

@@ -110,7 +110,7 @@ certs signed by this path; for instance,
the non-repudiation flag;
3) Extensions requested in the CSR will be copied
into the issued certificate.`,
}
} // TODO: Re-Write This (!)
fields["signature_bits"] = &framework.FieldSchema{
Type: framework.TypeInt,

3
changelog/28237.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:feature
**Set KeyUsage on PKI-CAs**: Key Usage can now be set on intermediate and root CAs, and CSRs generated by the PKI secret's engine.
```

8
go.sum
View File

@@ -1531,8 +1531,6 @@ github.com/hashicorp/nomad/api v0.0.0-20240213164230-c364cb57298d/go.mod h1:ijDw
github.com/hashicorp/raft v1.0.1/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI=
github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
github.com/hashicorp/raft v1.7.0 h1:4u24Qn6lQ6uwziM++UgsyiT64Q8GyRn43CV41qPiz1o=
github.com/hashicorp/raft v1.7.0/go.mod h1:N1sKh6Vn47mrWvEArQgILTyng8GoDRNYlgKyK7PMjs0=
github.com/hashicorp/raft v1.7.1 h1:ytxsNx4baHsRZrhUcbt3+79zc4ly8qm7pi0393pSchY=
github.com/hashicorp/raft v1.7.1/go.mod h1:hUeiEwQQR/Nk2iKDD0dkEhklSsu3jcAcqvPzPoZSAEM=
github.com/hashicorp/raft-autopilot v0.2.0 h1:2/R2RPgamgRKgNWGQioULZvjeKXQZmDuw5Ty+6c+H7Y=
@@ -1678,8 +1676,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc=
github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk=
github.com/jarcoal/httpmock v1.0.7 h1:d1a2VFpSdm5gtjhCPWsQHSnx8+5V3ms5431YwvmkuNk=
github.com/jarcoal/httpmock v1.0.7/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
@@ -1913,8 +1911,6 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/okta/okta-sdk-golang/v2 v2.12.1 h1:U+smE7trkHSZO8Mval3Ow85dbxawO+pMAr692VZq9gM=
github.com/okta/okta-sdk-golang/v2 v2.12.1/go.mod h1:KRoAArk1H216oiRnQT77UN6JAhBOnOWkK27yA1SM7FQ=
github.com/okta/okta-sdk-golang/v2 v2.19.0 h1:o3PQmItxfG82zwJFJhE1bkXHvtw8ExQO+IcVb+n6VRw=
github.com/okta/okta-sdk-golang/v2 v2.19.0/go.mod h1:h72I0ysswzPXKYN6MzWSX9QHRGDNILYZTz2Q8zFg7cI=
github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=

View File

@@ -17,7 +17,6 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"encoding/pem"
"errors"
@@ -1103,6 +1102,7 @@ func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Rea
}
// Like many root CAs, other information is ignored
csrTemplate := &x509.CertificateRequest{
Subject: data.Params.Subject,
DNSNames: data.Params.DNSNames,
@@ -1111,6 +1111,14 @@ func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Rea
URIs: data.Params.URIs,
}
if data.Params.KeyUsage != 0 {
keyUsageExt, err := marshalKeyUsage(data.Params.KeyUsage)
if err != nil {
return nil, fmt.Errorf("failed marshalling existing key usage: %w", err)
}
csrTemplate.ExtraExtensions = []pkix.Extension{keyUsageExt}
}
if err := HandleOtherCSRSANs(csrTemplate, data.Params.OtherSANs); err != nil {
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
}
@@ -1160,6 +1168,56 @@ func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Rea
return result, nil
}
// Marshal Key Usage taken from: https://cs.opensource.google/go/go/+/master:src/crypto/x509/x509.go;drc=370a6959e3edd9d901446661ee9fef3f72d150d4;l=1339
// It requires the two following functions (reverseBitsInAByte and asn1BitLength) below, taken from the same code base
// It's used for that code only for certificates; but we need to marshal key usage on CSR (createCSR above) as well
func marshalKeyUsage(ku x509.KeyUsage) (pkix.Extension, error) {
ext := pkix.Extension{Id: KeyUsageOID, Critical: true}
var a [2]byte
a[0] = reverseBitsInAByte(byte(ku))
a[1] = reverseBitsInAByte(byte(ku >> 8))
l := 1
if a[1] != 0 {
l = 2
}
bitString := a[:l]
var err error
ext.Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
return ext, err
}
// reverseBitsInAByte Taken from: https://cs.opensource.google/go/go/+/master:src/crypto/x509/x509.go;drc=370a6959e3edd9d901446661ee9fef3f72d150d4;l=1011
// needed for marshalKeyUsage called above
func reverseBitsInAByte(in byte) byte {
b1 := in>>4 | in<<4
b2 := b1>>2&0x33 | b1<<2&0xcc
b3 := b2>>1&0x55 | b2<<1&0xaa
return b3
}
// asn1BitLength returns the bit-length of bitString by considering the
// most-significant bit in a byte to be the "first" bit. This convention
// matches ASN.1, but differs from almost everything else.
func asn1BitLength(bitString []byte) int {
bitLen := len(bitString) * 8
for i := range bitString {
b := bitString[len(bitString)-i-1]
for bit := uint(0); bit < 8; bit++ {
if (b>>bit)&1 == 1 {
return bitLen
}
bitLen--
}
}
return 0
}
// SignCertificate performs the heavy lifting
// of generating a certificate from a CSR.
// Returns a ParsedCertBundle sans private keys.
@@ -1637,19 +1695,28 @@ func getKeyUsage(exts []pkix.Extension) (x509.KeyUsage, error) {
keyUsage := x509.KeyUsage(0)
for _, ext := range exts {
if ext.Id.Equal(KeyUsageOID) {
// ASN1 is Big Endian
// another example of equivalent code: https://cs.opensource.google/go/go/+/master:src/crypto/x509/parser.go;drc=dd84bb682482390bb8465482cb7b13d2e3b17297;l=319
buf := bytes.NewReader(ext.Value)
err := binary.Read(buf, binary.BigEndian, &keyUsage)
if err != nil {
return keyUsage, err
}
return keyUsage, nil
return parseKeyUsageExtension(ext.Value)
}
}
return keyUsage, nil
}
// Taken from: https://cs.opensource.google/go/go/+/master:src/crypto/x509/parser.go;drc=dd84bb682482390bb8465482cb7b13d2e3b17297;l=319
func parseKeyUsageExtension(der cryptobyte.String) (x509.KeyUsage, error) {
var usageBits asn1.BitString
if !der.ReadASN1BitString(&usageBits) {
return 0, errors.New("x509: invalid key usage")
}
var usage int
for i := 0; i < 9; i++ {
if usageBits.At(i) != 0 {
usage |= 1 << uint(i)
}
}
return x509.KeyUsage(usage), nil
}
func getExtKeyUsageOids(exts []pkix.Extension) ([]string, error) {
keyUsageOidStrings := make([]string, 0)
keyUsageOids := make([]asn1.ObjectIdentifier, 0)

View File

@@ -1101,6 +1101,12 @@ intermediary goes beyond the prescribed length.
set to one less than that of the signing certificate. A limit of `0` means a
literal path length of zero.
- `key_usage` `([]string: CRL,CertSign)` - This list of key usages will be added
to the existing set of key usages, CRL,CertSign, on the generated certificate.
Values can be found at https://golang.org/pkg/crypto/x509/#KeyUsage part of the
name. This is overwritten by use_csr_values if a key usage extension exists
within the csr.
- `exclude_cn_from_sans` `(bool: false)` - If true, the given `common_name` will
not be included in DNS or Email Subject Alternate Names (as appropriate).
Useful if the CN is not a hostname or email address, but is instead some
@@ -2470,6 +2476,11 @@ use the values set via `config/urls`.
less than that of the signing certificate. A limit of `0` means a literal
path length of zero.
- `key_usage` `([]string: CRL,CertSign)` - This list of key usages will be added
to the existing set of key usages, CRL,CertSign, on the generated certificate.
Values can be found at https://golang.org/pkg/crypto/x509/#KeyUsage part of the
name.
- `exclude_cn_from_sans` `(bool: false)` - If true, the given `common_name` will
not be included in DNS or Email Subject Alternate Names (as appropriate).
Useful if the CN is not a hostname or email address, but is instead some
@@ -2726,6 +2737,11 @@ based on this parameter.
extension with CA: true. Only needed as a workaround in some compatibility
scenarios with Active Directory Certificate Services.
- `key_usage` `([]string: )` - Specifies key_usage to encode in the
generated certificate. This is a list of the names of each key usage, valid
values can be found at https://golang.org/pkg/crypto/x509/#KeyUsage part of the
name. If not set, key usage will not appear on the CSR.
#### Managed keys parameters
See [Managed Keys](#managed-keys) for additional details on this feature, if