051bb9fc13
* Disallow adding CA's serial to revocation list * Allow disabling revocation list generation. This returns an empty (but signed) list, but does not affect tracking of revocations so turning it back on will populate the list properly.
255 lines
6.4 KiB
Go
255 lines
6.4 KiB
Go
package pki
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/errwrap"
|
|
"github.com/hashicorp/vault/helper/certutil"
|
|
"github.com/hashicorp/vault/helper/errutil"
|
|
"github.com/hashicorp/vault/logical"
|
|
"github.com/hashicorp/vault/logical/framework"
|
|
)
|
|
|
|
func pathGenerateIntermediate(b *backend) *framework.Path {
|
|
ret := &framework.Path{
|
|
Pattern: "intermediate/generate/" + framework.GenericNameRegex("exported"),
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.UpdateOperation: b.pathGenerateIntermediate,
|
|
},
|
|
|
|
HelpSynopsis: pathGenerateIntermediateHelpSyn,
|
|
HelpDescription: pathGenerateIntermediateHelpDesc,
|
|
}
|
|
|
|
ret.Fields = addCACommonFields(map[string]*framework.FieldSchema{})
|
|
ret.Fields = addCAKeyGenerationFields(ret.Fields)
|
|
ret.Fields["add_basic_constraints"] = &framework.FieldSchema{
|
|
Type: framework.TypeBool,
|
|
Description: `Whether to add a Basic Constraints
|
|
extension with CA: true. Only needed as a
|
|
workaround in some compatibility scenarios
|
|
with Active Directory Certificate Services.`,
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func pathSetSignedIntermediate(b *backend) *framework.Path {
|
|
ret := &framework.Path{
|
|
Pattern: "intermediate/set-signed",
|
|
|
|
Fields: map[string]*framework.FieldSchema{
|
|
"certificate": &framework.FieldSchema{
|
|
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.`,
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.UpdateOperation: b.pathSetSignedIntermediate,
|
|
},
|
|
|
|
HelpSynopsis: pathSetSignedIntermediateHelpSyn,
|
|
HelpDescription: pathSetSignedIntermediateHelpDesc,
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func (b *backend) pathGenerateIntermediate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
var err error
|
|
|
|
exported, format, role, errorResp := b.getGenerationParams(data)
|
|
if errorResp != nil {
|
|
return errorResp, nil
|
|
}
|
|
|
|
var resp *logical.Response
|
|
input := &dataBundle{
|
|
role: role,
|
|
req: req,
|
|
apiData: data,
|
|
}
|
|
parsedBundle, err := generateIntermediateCSR(b, input)
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case errutil.UserError:
|
|
return logical.ErrorResponse(err.Error()), nil
|
|
case errutil.InternalError:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
csrb, err := parsedBundle.ToCSRBundle()
|
|
if err != nil {
|
|
return nil, errwrap.Wrapf("error converting raw CSR bundle to CSR bundle: {{err}}", err)
|
|
}
|
|
|
|
resp = &logical.Response{
|
|
Data: map[string]interface{}{},
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
if data.Get("private_key_format").(string) == "pkcs8" {
|
|
err = convertRespToPKCS8(resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
cb := &certutil.CertBundle{}
|
|
cb.PrivateKey = csrb.PrivateKey
|
|
cb.PrivateKeyType = csrb.PrivateKeyType
|
|
|
|
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = req.Storage.Put(ctx, entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (b *backend) pathSetSignedIntermediate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
cert := data.Get("certificate").(string)
|
|
|
|
if cert == "" {
|
|
return logical.ErrorResponse("no certificate provided in the \"certificate\" parameter"), nil
|
|
}
|
|
|
|
inputBundle, err := certutil.ParsePEMBundle(cert)
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case errutil.InternalError:
|
|
return nil, err
|
|
default:
|
|
return logical.ErrorResponse(err.Error()), nil
|
|
}
|
|
}
|
|
|
|
if inputBundle.Certificate == nil {
|
|
return logical.ErrorResponse("supplied certificate could not be successfully parsed"), nil
|
|
}
|
|
|
|
cb := &certutil.CertBundle{}
|
|
entry, err := req.Storage.Get(ctx, "config/ca_bundle")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if entry == nil {
|
|
return logical.ErrorResponse("could not find any existing entry with a private key"), nil
|
|
}
|
|
|
|
err = entry.DecodeJSON(cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(cb.PrivateKey) == 0 || cb.PrivateKeyType == "" {
|
|
return logical.ErrorResponse("could not find an existing private key"), nil
|
|
}
|
|
|
|
parsedCB, err := cb.ToParsedCertBundle()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if parsedCB.PrivateKey == nil {
|
|
return nil, fmt.Errorf("saved key could not be parsed successfully")
|
|
}
|
|
|
|
inputBundle.PrivateKey = parsedCB.PrivateKey
|
|
inputBundle.PrivateKeyType = parsedCB.PrivateKeyType
|
|
inputBundle.PrivateKeyBytes = parsedCB.PrivateKeyBytes
|
|
|
|
if !inputBundle.Certificate.IsCA {
|
|
return logical.ErrorResponse("the given certificate is not marked for CA use and cannot be used with this backend"), nil
|
|
}
|
|
|
|
if err := inputBundle.Verify(); err != nil {
|
|
return nil, errwrap.Wrapf("verification of parsed bundle failed: {{err}}", err)
|
|
}
|
|
|
|
cb, err = inputBundle.ToCertBundle()
|
|
if err != nil {
|
|
return nil, errwrap.Wrapf("error converting raw values into cert bundle: {{err}}", err)
|
|
}
|
|
|
|
entry, err = logical.StorageEntryJSON("config/ca_bundle", cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = req.Storage.Put(ctx, entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entry.Key = "certs/" + normalizeSerial(cb.SerialNumber)
|
|
entry.Value = inputBundle.CertificateBytes
|
|
err = req.Storage.Put(ctx, entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// For ease of later use, also store just the certificate at a known
|
|
// location
|
|
entry.Key = "ca"
|
|
entry.Value = inputBundle.CertificateBytes
|
|
err = req.Storage.Put(ctx, entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Build a fresh CRL
|
|
err = buildCRL(ctx, b, req, true)
|
|
|
|
return nil, err
|
|
}
|
|
|
|
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.
|
|
`
|