Fix missing permitted_dns_domains parameter when signing certificates (#29436)

* Use PermittedDNSDomains parameter when signing certificates.

* Add missing name constraints extension docs for PKI root/generate.
This commit is contained in:
Victor Rodriguez
2025-01-28 17:54:20 -05:00
committed by GitHub
parent 838a38443f
commit f4fab41e7e
3 changed files with 149 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"math/big"
mathrand "math/rand"
"net"
"reflect"
"strings"
"sync"
@@ -27,6 +28,7 @@ import (
"time"
"github.com/fatih/structs"
"github.com/go-test/deep"
"github.com/hashicorp/vault/sdk/helper/cryptoutil"
)
@@ -1107,6 +1109,114 @@ func TestIgnoreCSRSigning(t *testing.T) {
})
}
// TestSignIntermediat_name_constraints verifies that all the name constraints extension fields are
// used when signing a certificate.
func TestSignCertificate_name_constraints(t *testing.T) {
t.Parallel()
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("failed generating ca key: %v", err)
}
subjKeyID, err := GetSubjKeyID(caKey)
if err != nil {
t.Fatalf("failed generating ca subject key id: %v", err)
}
caCertTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: "root.localhost",
},
SubjectKeyId: subjKeyID,
DNSNames: []string{"root.localhost"},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
SerialNumber: big.NewInt(mathrand.Int63()),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(262980 * time.Hour),
BasicConstraintsValid: true,
IsCA: true,
}
caBytes, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, caKey.Public(), caKey)
if err != nil {
t.Fatalf("failed creating ca certificate: %v", err)
}
caCert, err := x509.ParseCertificate(caBytes)
if err != nil {
t.Fatalf("failed parsing ca certificate: %v", err)
}
signingBundle := &CAInfoBundle{
ParsedCertBundle: ParsedCertBundle{
PrivateKeyType: ECPrivateKey,
PrivateKey: caKey,
CertificateBytes: caBytes,
Certificate: caCert,
CAChain: nil,
},
URLs: &URLEntries{},
}
key := genEdDSA(t)
csr := &x509.CertificateRequest{
PublicKeyAlgorithm: x509.ECDSA,
PublicKey: key.Public(),
Subject: pkix.Name{
CommonName: "test.dadgarcorp.com",
},
}
_, ipnet1, err := net.ParseCIDR("1.2.3.4/32")
if err != nil {
t.Fatal(err)
}
_, ipnet2, err := net.ParseCIDR("1.2.3.4/16")
if err != nil {
t.Fatal(err)
}
params := &CreationParameters{
IgnoreCSRSignature: true,
URLs: &URLEntries{},
NotAfter: time.Now().Add(10000 * time.Hour),
PermittedDNSDomains: []string{"example.com", ".example.com"},
ExcludedDNSDomains: []string{"bad.example.com"},
PermittedIPRanges: []*net.IPNet{ipnet1},
ExcludedIPRanges: []*net.IPNet{ipnet2},
PermittedEmailAddresses: []string{"one@example.com", "two@example.com"},
ExcludedEmailAddresses: []string{"un@example.com", "deux@example.com"},
PermittedURIDomains: []string{"domain1", "domain2"},
ExcludedURIDomains: []string{"domain3", "domain4"},
}
data := &CreationBundle{
Params: params,
SigningBundle: signingBundle,
CSR: csr,
}
parsedBundle, err := SignCertificate(data)
if err != nil {
t.Fatal("should have failed signing csr with ignore csr signature disabled")
}
var failedChecks []error
check := func(fieldName string, expected any, actual any) {
diff := deep.Equal(expected, actual)
if len(diff) > 0 {
failedChecks = append(failedChecks, fmt.Errorf("error in field %q: %v", fieldName, diff))
}
}
cert := parsedBundle.Certificate
check("PermittedDNSDomains", params.PermittedDNSDomains, cert.PermittedDNSDomains)
check("ExcludedDNSDomains", params.ExcludedDNSDomains, cert.ExcludedDNSDomains)
check("PermittedIPRanges", params.PermittedIPRanges, cert.PermittedIPRanges)
check("ExcludedIPRanges", params.ExcludedIPRanges, cert.ExcludedIPRanges)
check("PermittedEmailAddresses", params.PermittedEmailAddresses, cert.PermittedEmailAddresses)
check("ExcludedEmailAddresses", params.ExcludedEmailAddresses, cert.ExcludedEmailAddresses)
check("PermittedURIDomains", params.PermittedURIDomains, cert.PermittedURIDomains)
check("ExcludedURIDomains", params.ExcludedURIDomains, cert.ExcludedURIDomains)
if err := errors.Join(failedChecks...); err != nil {
t.Error(err)
}
}
func genRsaKey(t *testing.T) *rsa.PrivateKey {
key, err := cryptoutil.GenerateRSAKey(rand.Reader, 2048)
if err != nil {

View File

@@ -1360,6 +1360,7 @@ func signCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBun
certTemplate.IsCA = false
}
certTemplate.PermittedDNSDomains = append(certTemplate.PermittedDNSDomains, data.Params.PermittedDNSDomains...)
certTemplate.ExcludedDNSDomains = append(certTemplate.ExcludedDNSDomains, data.Params.ExcludedDNSDomains...)
certTemplate.PermittedIPRanges = append(certTemplate.PermittedIPRanges, data.Params.PermittedIPRanges...)
certTemplate.ExcludedIPRanges = append(certTemplate.ExcludedIPRanges, data.Params.ExcludedIPRanges...)

View File

@@ -2180,6 +2180,44 @@ use the values set via `config/urls`.
[RFC 5280 Section 4.2.1.10 - Name
Constraints](https://tools.ietf.org/html/rfc5280#section-4.2.1.10).
- `excluded_dns_domains` `(string: "")` - A comma separated string (or, string
array) containing DNS domains for which certificates are not allowed to be issued
or signed by this CA certificate. Supports subdomains via a `.` in front of
the domain, as per [RFC 5280 Section 4.2.1.10 - Name
Constraints](https://tools.ietf.org/html/rfc5280#section-4.2.1.10)
- `permitted_ip_ranges` `(string: "")` - A comma separated string (or, string
array) containing IP ranges for which certificates are allowed to be issued or
signed by this CA certificate. IP ranges must be in the CIDR notation of IP
address and prefix length like "192.0.2.0/24" or "2001:db8::/32", as defined
in RFC 4632 and RFC 4291.
- `excluded_ip_ranges` `(string: "")` - A comma separated string (or, string
array) containing IP ranges for which certificates are not allowed to be
issued or signed by this CA certificate. IP ranges must be in the CIDR
notation of IP address and prefix length like "192.0.2.0/24" or
"2001:db8::/32", as defined in RFC 4632 and RFC 4291.
- `permitted_email_addresses` `(string: "")` - A comma separated string (or, string
array) containing email addresses for which certificates are allowed to be issued or
signed by this CA certificate.
- `excluded_email_addresses` `(string: "")` - A comma separated string (or,
string array) containing email addresses for which certificates are not
allowed to be issued or signed by this CA certificate.
- `permitted_uri_domains` `(string: "")` - A comma separated string (or, string
array) containing fully qualified domain names for which certificates are
allowed to be issued or signed by this CA certificate. Supports subdomains via
a `.` in front of the domain, as per [RFC 5280 Section 4.2.1.10 - Name
Constraints](https://tools.ietf.org/html/rfc5280#section-4.2.1.10)
- `excluded_uri_domains` `(string: "")` - A comma separated string (or, string
array) containing fully qualified domain names for which certificates are not
allowed to be issued or signed by this CA certificate. Supports subdomains via
a `.` in front of the domain, as per [RFC 5280 Section 4.2.1.10 - Name
Constraints](https://tools.ietf.org/html/rfc5280#section-4.2.1.10)
- `ou` `(string: "")` - Specifies the OU (OrganizationalUnit) values in the
subject field of the resulting certificate. This is a comma-separated string
or JSON array.