8acbf7f480
* Add PSS signature support to Vault PKI engine Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Use issuer's RevocationSigAlg for CRL signing We introduce a new parameter on issuers, revocation_signature_algorithm to control the signature algorithm used during CRL signing. This is because the SignatureAlgorithm value from the certificate itself is incorrect for this purpose: a RSA root could sign an ECDSA intermediate with say, SHA256WithRSA, but when the intermediate goes to sign a CRL, it must use ECDSAWithSHA256 or equivalent instead of SHA256WithRSA. When coupled with support for PSS-only keys, allowing the user to set the signature algorithm value as desired seems like the best approach. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add use_pss, revocation_signature_algorithm docs Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add PSS to signature role issuance test matrix Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add changelog Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Allow roots to self-identify revocation alg When using PSS support with a managed key, sometimes the underlying device will not support PKCS#1v1.5 signatures. This results in CRL building failing, unless we update the entry's signature algorithm prior to building the CRL for the new root. With a RSA-type key and use_pss=true, we use the signature bits value to decide which hash function to use for PSS support. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add clearer error message on failed import When CRL building fails during cert/key import, due to PSS failures, give a better indication to the user that import succeeded its just CRL building that failed. This tells them the parameter to adjust on the issuer and warns that CRL building will fail until this is fixed. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add case insensitive SigAlgo matching Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Convert UsePSS back to regular bool Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Refactor PSS->certTemplate into helper function Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Proper string output on rev_sig_alg display Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Copy root's SignatureAlgorithm for CRL building Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
182 lines
5.4 KiB
Go
182 lines
5.4 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"
|
|
}
|
|
|
|
// Nasty hack part two. :-) For generation of CSRs, certutil presently doesn't
|
|
// support configuration of this. However, because we need generation parameters,
|
|
// which create a role and attempt to read this parameter, we need to provide
|
|
// a value (which will be ignored). Hence, we stub in the missing parameters here,
|
|
// including its schema, just enough for it to work..
|
|
data.Schema["signature_bits"] = &framework.FieldSchema{
|
|
Type: framework.TypeInt,
|
|
Default: 0,
|
|
}
|
|
data.Raw["signature_bits"] = 0
|
|
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, 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 := getURLs(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 access information 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 with this information.")
|
|
}
|
|
|
|
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
|
|
|
|
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.
|
|
`
|