From fc6d23a54ef34d13ac2b92ab1c9b2c8afc572939 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 1 Feb 2016 13:19:41 -0500 Subject: [PATCH] Allow the format to be specified as pem_bundle, which creates a concatenated PEM file. Fixes #992 --- builtin/logical/pki/ca_util.go | 2 +- builtin/logical/pki/cert_util.go | 1 + builtin/logical/pki/fields.go | 6 ++-- builtin/logical/pki/path_intermediate.go | 9 +++++ builtin/logical/pki/path_issue_sign.go | 31 +++++++++++----- builtin/logical/pki/path_root.go | 29 ++++++++++++--- website/source/docs/secrets/pki/index.html.md | 36 ++++++++++++------- 7 files changed, 85 insertions(+), 29 deletions(-) diff --git a/builtin/logical/pki/ca_util.go b/builtin/logical/pki/ca_util.go index 6e757f014..3a2c37a8c 100644 --- a/builtin/logical/pki/ca_util.go +++ b/builtin/logical/pki/ca_util.go @@ -22,7 +22,7 @@ func (b *backend) getGenerationParams( format = getFormat(data) if format == "" { errorResp = logical.ErrorResponse( - `The "format" path parameter must be "pem" or "der"`) + `The "format" path parameter must be "pem", "der", or "pem_bundle"`) return } diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index 9d8037b86..9d002669e 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -74,6 +74,7 @@ func getFormat(data *framework.FieldData) string { switch format { case "pem": case "der": + case "pem_bundle": default: format = "" } diff --git a/builtin/logical/pki/fields.go b/builtin/logical/pki/fields.go index cef49144e..bf9b557bd 100644 --- a/builtin/logical/pki/fields.go +++ b/builtin/logical/pki/fields.go @@ -8,8 +8,10 @@ func addIssueAndSignCommonFields(fields map[string]*framework.FieldSchema) map[s fields["format"] = &framework.FieldSchema{ Type: framework.TypeString, Default: "pem", - Description: `Format for returned data. Can be "pem" or "der"; -defaults to "pem".`, + Description: `Format for returned data. Can be "pem", "der", +or "pem_bundle". If "pem_bundle" any private +private key and issuing cert will be appended +to the certificate pem. Defaults to "pem".`, } fields["ip_sans"] = &framework.FieldSchema{ diff --git a/builtin/logical/pki/path_intermediate.go b/builtin/logical/pki/path_intermediate.go index 929ffe276..ff27f9d5c 100644 --- a/builtin/logical/pki/path_intermediate.go +++ b/builtin/logical/pki/path_intermediate.go @@ -88,6 +88,15 @@ func (b *backend) pathGenerateIntermediate( 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 { diff --git a/builtin/logical/pki/path_issue_sign.go b/builtin/logical/pki/path_issue_sign.go index c6da42c94..c85dd8744 100644 --- a/builtin/logical/pki/path_issue_sign.go +++ b/builtin/logical/pki/path_issue_sign.go @@ -133,7 +133,7 @@ func (b *backend) pathIssueSignCert( format := getFormat(data) if format == "" { return logical.ErrorResponse( - `The "format" path parameter must be "pem" or "der"`), nil + `The "format" path parameter must be "pem", "der", or "pem_bundle"`), nil } var caErr error @@ -170,20 +170,33 @@ func (b *backend) pathIssueSignCert( resp := b.Secret(SecretCertsType).Response( map[string]interface{}{ - "certificate": cb.Certificate, - "issuing_ca": cb.IssuingCA, - "serial_number": cb.SerialNumber, + "certificate": cb.Certificate, + "issuing_ca": cb.IssuingCA, }, map[string]interface{}{ "serial_number": cb.SerialNumber, }) - if !useCSR { - resp.Data["private_key"] = cb.PrivateKey - resp.Data["private_key_type"] = cb.PrivateKeyType - } + switch format { + case "pem": + resp.Data["issuing_ca"] = cb.IssuingCA + resp.Data["certificate"] = cb.Certificate - if format == "der" { + if !useCSR { + resp.Data["private_key"] = cb.PrivateKey + resp.Data["private_key_type"] = cb.PrivateKeyType + } + + case "pem_bundle": + resp.Data["issuing_ca"] = cb.IssuingCA + resp.Data["certificate"] = fmt.Sprintf("%s\n%s", cb.Certificate, cb.IssuingCA) + if !useCSR { + resp.Data["private_key"] = cb.PrivateKey + resp.Data["private_key_type"] = cb.PrivateKeyType + resp.Data["certificate"] = fmt.Sprintf("%s\n%s\n%s", cb.PrivateKey, cb.Certificate, cb.IssuingCA) + } + + case "der": resp.Data["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes) resp.Data["issuing_ca"] = base64.StdEncoding.EncodeToString(parsedBundle.IssuingCABytes) if !useCSR { diff --git a/builtin/logical/pki/path_root.go b/builtin/logical/pki/path_root.go index d4dee5a6e..6bc14d535 100644 --- a/builtin/logical/pki/path_root.go +++ b/builtin/logical/pki/path_root.go @@ -101,8 +101,6 @@ func (b *backend) pathCAGenerateRoot( map[string]interface{}{ "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), "serial_number": cb.SerialNumber, - "certificate": cb.Certificate, - "issuing_ca": cb.IssuingCA, }, map[string]interface{}{ "serial_number": cb.SerialNumber, @@ -110,10 +108,24 @@ func (b *backend) pathCAGenerateRoot( switch format { case "pem": + resp.Data["certificate"] = cb.Certificate + resp.Data["issuing_ca"] = cb.IssuingCA if exported { resp.Data["private_key"] = cb.PrivateKey resp.Data["private_key_type"] = cb.PrivateKeyType } + + case "pem_bundle": + resp.Data["issuing_ca"] = cb.IssuingCA + + if exported { + resp.Data["private_key"] = cb.PrivateKey + resp.Data["private_key_type"] = cb.PrivateKeyType + resp.Data["certificate"] = fmt.Sprintf("%s\n%s\n%s", cb.PrivateKey, cb.Certificate, cb.IssuingCA) + } else { + resp.Data["certificate"] = fmt.Sprintf("%s\n%s", cb.Certificate, cb.IssuingCA) + } + case "der": resp.Data["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes) resp.Data["issuing_ca"] = base64.StdEncoding.EncodeToString(parsedBundle.IssuingCABytes) @@ -228,14 +240,21 @@ func (b *backend) pathCASignIntermediate( map[string]interface{}{ "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), "serial_number": cb.SerialNumber, - "certificate": cb.Certificate, - "issuing_ca": cb.IssuingCA, }, map[string]interface{}{ "serial_number": cb.SerialNumber, }) - if format == "der" { + switch format { + case "pem": + resp.Data["certificate"] = cb.Certificate + resp.Data["issuing_ca"] = cb.IssuingCA + + case "pem_bundle": + resp.Data["certificate"] = fmt.Sprintf("%s\n%s", cb.Certificate, cb.IssuingCA) + resp.Data["issuing_ca"] = cb.IssuingCA + + case "der": resp.Data["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes) resp.Data["issuing_ca"] = base64.StdEncoding.EncodeToString(parsedBundle.IssuingCABytes) } diff --git a/website/source/docs/secrets/pki/index.html.md b/website/source/docs/secrets/pki/index.html.md index 5a97de9eb..f9b359854 100644 --- a/website/source/docs/secrets/pki/index.html.md +++ b/website/source/docs/secrets/pki/index.html.md @@ -741,8 +741,10 @@ subpath for interactive help output.
  • format optional - Format for returned data. Can be `pem` or `der`; defaults to `pem`. If - `der`, the output is base64 encoded. + Format for returned data. Can be `pem`, `der`, or `pem_bundle`; + defaults to `pem`. If `der`, the output is base64 encoded. If + `pem_bundle`, the `csr` field will contain the private key (if + exported) and CSR, concatenated.
  • key_type @@ -860,8 +862,10 @@ subpath for interactive help output.
  • format optional - Format for returned data. Can be `pem` or `der`; defaults to `pem`. If - `der`, the output is base64 encoded. + Format for returned data. Can be `pem`, `der`, or `pem_bundle`; + defaults to `pem`. If `der`, the output is base64 encoded. If + `pem_bundle`, the `certificate` field will contain the private key, + certificate, and issuing CA, concatenated.
  • @@ -1233,8 +1237,10 @@ subpath for interactive help output.
  • format optional - Format for returned data. Can be `pem` or `der`; defaults to `pem`. If - `der`, the output is base64 encoded. + Format for returned data. Can be `pem`, `der`, or `pem_bundle`; + defaults to `pem`. If `der`, the output is base64 encoded. If + `pem_bundle`, the `certificate` field will contain the private key (if exported), + certificate, and issuing CA, concatenated.
  • key_type @@ -1333,8 +1339,10 @@ subpath for interactive help output.
  • format optional - Format for returned data. Can be `pem` or `der`; defaults to `pem`. If - `der`, the output is base64 encoded. + Format for returned data. Can be `pem`, `der`, or `pem_bundle`; + defaults to `pem`. If `der`, the output is base64 encoded. If + `pem_bundle`, the `certificate` field will contain the certificate and + issuing CA, concatenated.
  • max_path_length @@ -1435,8 +1443,10 @@ subpath for interactive help output.
  • format optional - Format for returned data. Can be `pem` or `der`; defaults to `pem`. If - `der`, the output is base64 encoded. + Format for returned data. Can be `pem`, `der`, or `pem_bundle`; + defaults to `pem`. If `der`, the output is base64 encoded. If + `pem_bundle`, the `certificate` field will contain the certificate and + issuing CA, concatenated.
  • @@ -1499,8 +1509,10 @@ subpath for interactive help output.
  • format optional - Format for returned data. Can be `pem` or `der`; defaults to `pem`. If - `der`, the output is base64 encoded. + Format for returned data. Can be `pem`, `der`, or `pem_bundle`; + defaults to `pem`. If `der`, the output is base64 encoded. If + `pem_bundle`, the `certificate` field will contain the certificate and + issuing CA, concatenated.