Steven Clark cbf6dc2c4f
PKI refactoring to start breaking apart monolith into sub-packages (#24406)
* PKI refactoring to start breaking apart monolith into sub-packages

 - This was broken down by commit within enterprise for ease of review
   but would be too difficult to bring back individual commits back
   to the CE repository. (they would be squashed anyways)
 - This change was created by exporting a patch of the enterprise PR
   and applying it to CE repository

* Fix TestBackend_OID_SANs to not be rely on map ordering
2023-12-07 09:22:53 -05:00

292 lines
8.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package issuing
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"fmt"
"github.com/hashicorp/vault/sdk/helper/certutil"
"github.com/hashicorp/vault/sdk/helper/errutil"
"github.com/hashicorp/vault/sdk/logical"
)
type SignCertInput interface {
CreationBundleInput
GetCSR() (*x509.CertificateRequest, error)
IsCA() bool
UseCSRValues() bool
GetPermittedDomains() []string
}
func NewBasicSignCertInput(csr *x509.CertificateRequest, isCA bool, useCSRValues bool) BasicSignCertInput {
return BasicSignCertInput{
isCA: isCA,
useCSRValues: useCSRValues,
csr: csr,
}
}
var _ SignCertInput = BasicSignCertInput{}
type BasicSignCertInput struct {
isCA bool
useCSRValues bool
csr *x509.CertificateRequest
}
func (b BasicSignCertInput) GetTTL() int {
return 0
}
func (b BasicSignCertInput) GetOptionalNotAfter() (interface{}, bool) {
return "", false
}
func (b BasicSignCertInput) GetCommonName() string {
return ""
}
func (b BasicSignCertInput) GetSerialNumber() string {
return ""
}
func (b BasicSignCertInput) GetExcludeCnFromSans() bool {
return false
}
func (b BasicSignCertInput) GetOptionalAltNames() (interface{}, bool) {
return []string{}, false
}
func (b BasicSignCertInput) GetOtherSans() []string {
return []string{}
}
func (b BasicSignCertInput) GetIpSans() []string {
return []string{}
}
func (b BasicSignCertInput) GetURISans() []string {
return []string{}
}
func (b BasicSignCertInput) GetOptionalSkid() (interface{}, bool) {
return "", false
}
func (b BasicSignCertInput) IsUserIdInSchema() (interface{}, bool) {
return []string{}, false
}
func (b BasicSignCertInput) GetUserIds() []string {
return []string{}
}
func (b BasicSignCertInput) GetCSR() (*x509.CertificateRequest, error) {
return b.csr, nil
}
func (b BasicSignCertInput) IsCA() bool {
return b.isCA
}
func (b BasicSignCertInput) UseCSRValues() bool {
return b.useCSRValues
}
func (b BasicSignCertInput) GetPermittedDomains() []string {
return []string{}
}
func SignCert(b logical.SystemView, role *RoleEntry, entityInfo EntityInfo, caSign *certutil.CAInfoBundle, signInput SignCertInput) (*certutil.ParsedCertBundle, []string, error) {
if role == nil {
return nil, nil, errutil.InternalError{Err: "no role found in data bundle"}
}
csr, err := signInput.GetCSR()
if err != nil {
return nil, nil, err
}
if csr.PublicKeyAlgorithm == x509.UnknownPublicKeyAlgorithm || csr.PublicKey == nil {
return nil, nil, errutil.UserError{Err: "Refusing to sign CSR with empty PublicKey. This usually means the SubjectPublicKeyInfo field has an OID not recognized by Go, such as 1.2.840.113549.1.1.10 for rsaPSS."}
}
// This switch validates that the CSR key type matches the role and sets
// the Value in the actualKeyType/actualKeyBits values.
actualKeyType := ""
actualKeyBits := 0
switch role.KeyType {
case "rsa":
// Verify that the key matches the role type
if csr.PublicKeyAlgorithm != x509.RSA {
return nil, nil, errutil.UserError{Err: fmt.Sprintf("role requires keys of type %s", role.KeyType)}
}
pubKey, ok := csr.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, nil, errutil.UserError{Err: "could not parse CSR's public key"}
}
actualKeyType = "rsa"
actualKeyBits = pubKey.N.BitLen()
case "ec":
// Verify that the key matches the role type
if csr.PublicKeyAlgorithm != x509.ECDSA {
return nil, nil, errutil.UserError{Err: fmt.Sprintf(
"role requires keys of type %s",
role.KeyType)}
}
pubKey, ok := csr.PublicKey.(*ecdsa.PublicKey)
if !ok {
return nil, nil, errutil.UserError{Err: "could not parse CSR's public key"}
}
actualKeyType = "ec"
actualKeyBits = pubKey.Params().BitSize
case "ed25519":
// Verify that the key matches the role type
if csr.PublicKeyAlgorithm != x509.Ed25519 {
return nil, nil, errutil.UserError{Err: fmt.Sprintf(
"role requires keys of type %s",
role.KeyType)}
}
_, ok := csr.PublicKey.(ed25519.PublicKey)
if !ok {
return nil, nil, errutil.UserError{Err: "could not parse CSR's public key"}
}
actualKeyType = "ed25519"
actualKeyBits = 0
case "any":
// We need to compute the actual key type and key bits, to correctly
// validate minimums and SignatureBits below.
switch csr.PublicKeyAlgorithm {
case x509.RSA:
pubKey, ok := csr.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, nil, errutil.UserError{Err: "could not parse CSR's public key"}
}
if pubKey.N.BitLen() < 2048 {
return nil, nil, errutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"}
}
actualKeyType = "rsa"
actualKeyBits = pubKey.N.BitLen()
case x509.ECDSA:
pubKey, ok := csr.PublicKey.(*ecdsa.PublicKey)
if !ok {
return nil, nil, errutil.UserError{Err: "could not parse CSR's public key"}
}
actualKeyType = "ec"
actualKeyBits = pubKey.Params().BitSize
case x509.Ed25519:
_, ok := csr.PublicKey.(ed25519.PublicKey)
if !ok {
return nil, nil, errutil.UserError{Err: "could not parse CSR's public key"}
}
actualKeyType = "ed25519"
actualKeyBits = 0
default:
return nil, nil, errutil.UserError{Err: "Unknown key type in CSR: " + csr.PublicKeyAlgorithm.String()}
}
default:
return nil, nil, errutil.InternalError{Err: fmt.Sprintf("unsupported key type Value: %s", role.KeyType)}
}
// Before validating key lengths, update our KeyBits/SignatureBits based
// on the actual CSR key type.
if role.KeyType == "any" {
// We update the Value of KeyBits and SignatureBits here (from the
// role), using the specified key type. This allows us to convert
// the default Value (0) for SignatureBits and KeyBits to a
// meaningful Value.
//
// We ignore the role's original KeyBits Value if the KeyType is any
// as legacy (pre-1.10) roles had default values that made sense only
// for RSA keys (key_bits=2048) and the older code paths ignored the role Value
// set for KeyBits when KeyType was set to any. This also enforces the
// docs saying when key_type=any, we only enforce our specified minimums
// for signing operations
var err error
if role.KeyBits, role.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(
actualKeyType, 0, role.SignatureBits); err != nil {
return nil, nil, errutil.InternalError{Err: fmt.Sprintf("unknown internal error updating default values: %v", err)}
}
// We're using the KeyBits field as a minimum Value below, and P-224 is safe
// and a previously allowed Value. However, the above call defaults
// to P-256 as that's a saner default than P-224 (w.r.t. generation), so
// override it here to allow 224 as the smallest size we permit.
if actualKeyType == "ec" {
role.KeyBits = 224
}
}
// At this point, role.KeyBits and role.SignatureBits should both
// be non-zero, for RSA and ECDSA keys. Validate the actualKeyBits based on
// the role's values. If the KeyType was any, and KeyBits was set to 0,
// KeyBits should be updated to 2048 unless some other Value was chosen
// explicitly.
//
// This validation needs to occur regardless of the role's key type, so
// that we always validate both RSA and ECDSA key sizes.
if actualKeyType == "rsa" {
if actualKeyBits < role.KeyBits {
return nil, nil, errutil.UserError{Err: fmt.Sprintf(
"role requires a minimum of a %d-bit key, but CSR's key is %d bits",
role.KeyBits, actualKeyBits)}
}
if actualKeyBits < 2048 {
return nil, nil, errutil.UserError{Err: fmt.Sprintf(
"Vault requires a minimum of a 2048-bit key, but CSR's key is %d bits",
actualKeyBits)}
}
} else if actualKeyType == "ec" {
if actualKeyBits < role.KeyBits {
return nil, nil, errutil.UserError{Err: fmt.Sprintf(
"role requires a minimum of a %d-bit key, but CSR's key is %d bits",
role.KeyBits,
actualKeyBits)}
}
}
creation, warnings, err := GenerateCreationBundle(b, role, entityInfo, signInput, caSign, csr)
if err != nil {
return nil, nil, err
}
if creation.Params == nil {
return nil, nil, errutil.InternalError{Err: "nil parameters received from parameter bundle generation"}
}
creation.Params.IsCA = signInput.IsCA()
creation.Params.UseCSRValues = signInput.UseCSRValues()
if signInput.IsCA() {
creation.Params.PermittedDNSDomains = signInput.GetPermittedDomains()
} else {
for _, ext := range csr.Extensions {
if ext.Id.Equal(certutil.ExtensionBasicConstraintsOID) {
warnings = append(warnings, "specified CSR contained a Basic Constraints extension that was ignored during issuance")
}
}
}
parsedBundle, err := certutil.SignCertificate(creation)
if err != nil {
return nil, nil, err
}
return parsedBundle, warnings, nil
}