open-vault/builtin/logical/pki/path_intermediate.go
Steven Clark 10ecf10248
PKI: Add support for signature_bits param to the intermediate/generate api (#17388)
* PKI: Add support for signature_bits param to the intermediate/generate api

 - Mainly to work properly with GCP backed managed keys, we need to
   issue signatures that would match the GCP key algorithm.
 - At this time due to https://github.com/golang/go/issues/45990 we
   can't issue PSS signed CSRs, as the libraries in Go always request
   a PKCS1v15.
 - Add an extra check in intermediate/generate that validates the CSR's
   signature before providing it back to the client in case we generated
   a bad signature such as if an end-user used a GCP backed managed key
   with a RSA PSS algorithm.
   - GCP ignores the requested signature type and always signs with the
     key's algorithm which can lead to a CSR that says it is signed with
     a PKCS1v15 algorithm but is actually a RSA PSS signature

* Add cl

* PR feedback
2022-10-03 12:39:54 -04:00

176 lines
5.2 KiB
Go

package pki
import (
"context"
"encoding/base64"
"fmt"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/errutil"
"github.com/hashicorp/vault/sdk/logical"
)
func pathGenerateIntermediate(b *backend) *framework.Path {
return buildPathGenerateIntermediate(b, "intermediate/generate/"+framework.GenericNameRegex("exported"))
}
func pathSetSignedIntermediate(b *backend) *framework.Path {
ret := &framework.Path{
Pattern: "intermediate/set-signed",
Fields: map[string]*framework.FieldSchema{
"certificate": {
Type: framework.TypeString,
Description: `PEM-format certificate. This must be a CA
certificate with a public key matching the
previously-generated key from the generation
endpoint. Additional parent CAs may be optionally
appended to the bundle.`,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathImportIssuers,
// Read more about why these flags are set in backend.go
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
},
HelpSynopsis: pathSetSignedIntermediateHelpSyn,
HelpDescription: pathSetSignedIntermediateHelpDesc,
}
return ret
}
func (b *backend) pathGenerateIntermediate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Since we're planning on updating issuers here, grab the lock so we've
// got a consistent view.
b.issuersLock.Lock()
defer b.issuersLock.Unlock()
var err error
if b.useLegacyBundleCaStorage() {
return logical.ErrorResponse("Can not create intermediate until migration has completed"), nil
}
// Nasty hack :-) For cross-signing, we want to use the existing key, but
// this isn't _actually_ part of the path. Put it into the request
// parameters as if it was.
if req.Path == "intermediate/cross-sign" {
data.Raw["exported"] = "existing"
}
// Remove this once https://github.com/golang/go/issues/45990 is fixed
data.Schema["use_pss"] = &framework.FieldSchema{
Type: framework.TypeBool,
Default: false,
}
data.Raw["use_pss"] = false
sc := b.makeStorageContext(ctx, req.Storage)
exported, format, role, errorResp := getGenerationParams(sc, data)
if errorResp != nil {
return errorResp, nil
}
keyName, err := getKeyName(sc, data)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}
var resp *logical.Response
input := &inputBundle{
role: role,
req: req,
apiData: data,
}
parsedBundle, warnings, err := generateIntermediateCSR(sc, input, b.Backend.GetRandomReader())
if err != nil {
switch err.(type) {
case errutil.UserError:
return logical.ErrorResponse(err.Error()), nil
default:
return nil, err
}
}
csrb, err := parsedBundle.ToCSRBundle()
if err != nil {
return nil, fmt.Errorf("error converting raw CSR bundle to CSR bundle: %w", err)
}
resp = &logical.Response{
Data: map[string]interface{}{},
}
entries, err := getGlobalAIAURLs(ctx, req.Storage)
if err == nil && len(entries.OCSPServers) == 0 && len(entries.IssuingCertificates) == 0 && len(entries.CRLDistributionPoints) == 0 {
// If the operator hasn't configured any of the URLs prior to
// generating this issuer, we should add a warning to the response,
// informing them they might want to do so and re-generate the issuer.
resp.AddWarning("This mount hasn't configured any authority information access (AIA) fields; this may make it harder for systems to find missing certificates in the chain or to validate revocation status of certificates. Consider updating /config/urls or the newly generated issuer with this information. Since this certificate is an intermediate, it might be useful to regenerate this certificate after fixing this problem for the root mount.")
}
switch format {
case "pem":
resp.Data["csr"] = csrb.CSR
if exported {
resp.Data["private_key"] = csrb.PrivateKey
resp.Data["private_key_type"] = csrb.PrivateKeyType
}
case "pem_bundle":
resp.Data["csr"] = csrb.CSR
if exported {
resp.Data["csr"] = fmt.Sprintf("%s\n%s", csrb.PrivateKey, csrb.CSR)
resp.Data["private_key"] = csrb.PrivateKey
resp.Data["private_key_type"] = csrb.PrivateKeyType
}
case "der":
resp.Data["csr"] = base64.StdEncoding.EncodeToString(parsedBundle.CSRBytes)
if exported {
resp.Data["private_key"] = base64.StdEncoding.EncodeToString(parsedBundle.PrivateKeyBytes)
resp.Data["private_key_type"] = csrb.PrivateKeyType
}
default:
return nil, fmt.Errorf("unsupported format argument: %s", format)
}
if data.Get("private_key_format").(string) == "pkcs8" {
err = convertRespToPKCS8(resp)
if err != nil {
return nil, err
}
}
myKey, _, err := sc.importKey(csrb.PrivateKey, keyName, csrb.PrivateKeyType)
if err != nil {
return nil, err
}
resp.Data["key_id"] = myKey.ID
resp = addWarnings(resp, warnings)
return resp, nil
}
const pathGenerateIntermediateHelpSyn = `
Generate a new CSR and private key used for signing.
`
const pathGenerateIntermediateHelpDesc = `
See the API documentation for more information.
`
const pathSetSignedIntermediateHelpSyn = `
Provide the signed intermediate CA cert.
`
const pathSetSignedIntermediateHelpDesc = `
See the API documentation for more information.
`