package pki import ( "context" "encoding/base64" "fmt" "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/certutil" "github.com/hashicorp/vault/sdk/helper/errutil" "github.com/hashicorp/vault/sdk/logical" ) 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. `