2015-06-17 16:43:36 +00:00
|
|
|
package certutil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto"
|
2021-10-07 19:17:31 +00:00
|
|
|
"crypto/dsa"
|
2015-11-16 21:32:49 +00:00
|
|
|
"crypto/ecdsa"
|
2020-08-11 05:14:17 +00:00
|
|
|
"crypto/ed25519"
|
2015-11-16 21:32:49 +00:00
|
|
|
"crypto/elliptic"
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/rsa"
|
2015-06-17 16:43:36 +00:00
|
|
|
"crypto/sha1"
|
|
|
|
"crypto/x509"
|
2019-05-09 15:43:11 +00:00
|
|
|
"crypto/x509/pkix"
|
|
|
|
"encoding/asn1"
|
2015-06-17 16:43:36 +00:00
|
|
|
"encoding/pem"
|
2018-07-06 14:25:32 +00:00
|
|
|
"errors"
|
2015-06-17 16:43:36 +00:00
|
|
|
"fmt"
|
2020-02-13 17:56:29 +00:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2015-11-16 21:32:49 +00:00
|
|
|
"math/big"
|
2019-05-09 15:43:11 +00:00
|
|
|
"net"
|
|
|
|
"net/url"
|
2015-10-08 01:50:57 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2019-05-09 15:43:11 +00:00
|
|
|
"time"
|
2015-06-17 16:43:36 +00:00
|
|
|
|
2019-05-09 15:43:11 +00:00
|
|
|
"github.com/hashicorp/errwrap"
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/errutil"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
2015-06-17 16:43:36 +00:00
|
|
|
"github.com/mitchellh/mapstructure"
|
2019-05-09 15:43:11 +00:00
|
|
|
"golang.org/x/crypto/cryptobyte"
|
|
|
|
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
|
2015-06-17 16:43:36 +00:00
|
|
|
)
|
|
|
|
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
const rsaMinimumSecureKeySize = 2048
|
|
|
|
|
|
|
|
// Mapping of key types to default key lengths
|
2022-01-14 23:35:27 +00:00
|
|
|
var defaultAlgorithmKeyBits = map[string]int{
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
"rsa": 2048,
|
2022-01-14 23:35:27 +00:00
|
|
|
"ec": 256,
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 17:18:38 +00:00
|
|
|
// Mapping of NIST P-Curve's key length to expected signature bits.
|
|
|
|
var expectedNISTPCurveHashBits = map[int]int{
|
|
|
|
224: 256,
|
|
|
|
256: 256,
|
|
|
|
384: 384,
|
|
|
|
521: 512,
|
|
|
|
}
|
|
|
|
|
2016-09-16 15:05:43 +00:00
|
|
|
// GetHexFormatted returns the byte buffer formatted in hex with
|
2015-06-17 16:43:36 +00:00
|
|
|
// the specified separator between bytes.
|
2016-09-16 15:05:43 +00:00
|
|
|
func GetHexFormatted(buf []byte, sep string) string {
|
2015-06-17 16:43:36 +00:00
|
|
|
var ret bytes.Buffer
|
|
|
|
for _, cur := range buf {
|
|
|
|
if ret.Len() > 0 {
|
|
|
|
fmt.Fprintf(&ret, sep)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(&ret, "%02x", cur)
|
|
|
|
}
|
|
|
|
return ret.String()
|
|
|
|
}
|
|
|
|
|
2018-02-26 07:23:06 +00:00
|
|
|
// ParseHexFormatted returns the raw bytes from a formatted hex string
|
2015-10-08 01:50:57 +00:00
|
|
|
func ParseHexFormatted(in, sep string) []byte {
|
|
|
|
var ret bytes.Buffer
|
|
|
|
var err error
|
|
|
|
var inBits int64
|
|
|
|
inBytes := strings.Split(in, sep)
|
|
|
|
for _, inByte := range inBytes {
|
|
|
|
if inBits, err = strconv.ParseInt(inByte, 16, 8); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
2018-02-26 07:23:06 +00:00
|
|
|
ret.WriteByte(byte(inBits))
|
2015-10-08 01:50:57 +00:00
|
|
|
}
|
|
|
|
return ret.Bytes()
|
|
|
|
}
|
|
|
|
|
2022-01-28 15:46:51 +00:00
|
|
|
// GetSubjKeyID returns the subject key ID. The computed ID is the SHA-1 hash of
|
|
|
|
// the marshaled public key according to
|
|
|
|
// https://tools.ietf.org/html/rfc5280#section-4.2.1.2 (1)
|
2015-06-17 16:43:36 +00:00
|
|
|
func GetSubjKeyID(privateKey crypto.Signer) ([]byte, error) {
|
|
|
|
if privateKey == nil {
|
2018-02-05 19:26:31 +00:00
|
|
|
return nil, errutil.InternalError{Err: "passed-in private key is nil"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
2022-01-28 15:46:51 +00:00
|
|
|
return getSubjectKeyID(privateKey.Public())
|
|
|
|
}
|
2015-06-18 14:44:02 +00:00
|
|
|
|
2022-01-28 15:46:51 +00:00
|
|
|
func getSubjectKeyID(pub interface{}) ([]byte, error) {
|
|
|
|
var publicKeyBytes []byte
|
|
|
|
switch pub := pub.(type) {
|
|
|
|
case *rsa.PublicKey:
|
|
|
|
type pkcs1PublicKey struct {
|
|
|
|
N *big.Int
|
|
|
|
E int
|
|
|
|
}
|
2015-06-17 16:43:36 +00:00
|
|
|
|
2022-01-28 15:46:51 +00:00
|
|
|
var err error
|
|
|
|
publicKeyBytes, err = asn1.Marshal(pkcs1PublicKey{
|
|
|
|
N: pub.N,
|
|
|
|
E: pub.E,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("error marshalling public key: %s", err)}
|
|
|
|
}
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
|
|
|
case ed25519.PublicKey:
|
|
|
|
publicKeyBytes = pub
|
|
|
|
default:
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported public key type: %T", pub)}
|
|
|
|
}
|
|
|
|
skid := sha1.Sum(publicKeyBytes)
|
|
|
|
return skid[:], nil
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParsePKIMap takes a map (for instance, the Secret.Data
|
|
|
|
// returned from the PKI backend) and returns a ParsedCertBundle.
|
|
|
|
func ParsePKIMap(data map[string]interface{}) (*ParsedCertBundle, error) {
|
|
|
|
result := &CertBundle{}
|
|
|
|
err := mapstructure.Decode(data, result)
|
|
|
|
if err != nil {
|
2018-02-05 19:26:31 +00:00
|
|
|
return nil, errutil.UserError{Err: err.Error()}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result.ToParsedCertBundle()
|
|
|
|
}
|
|
|
|
|
2015-11-16 17:45:31 +00:00
|
|
|
// ParsePKIJSON takes a JSON-encoded string and returns a ParsedCertBundle.
|
2015-06-17 16:43:36 +00:00
|
|
|
//
|
|
|
|
// This can be either the output of an
|
|
|
|
// issue call from the PKI backend or just its data member; or,
|
|
|
|
// JSON not coming from the PKI backend.
|
|
|
|
func ParsePKIJSON(input []byte) (*ParsedCertBundle, error) {
|
|
|
|
result := &CertBundle{}
|
2016-07-06 16:25:40 +00:00
|
|
|
err := jsonutil.DecodeJSON(input, &result)
|
2015-06-17 16:43:36 +00:00
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
return result.ToParsedCertBundle()
|
|
|
|
}
|
|
|
|
|
2015-06-18 10:42:57 +00:00
|
|
|
var secret Secret
|
2016-07-06 16:25:40 +00:00
|
|
|
err = jsonutil.DecodeJSON(input, &secret)
|
2015-06-17 16:43:36 +00:00
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
return ParsePKIMap(secret.Data)
|
|
|
|
}
|
|
|
|
|
2018-02-05 19:26:31 +00:00
|
|
|
return nil, errutil.UserError{Err: "unable to parse out of either secret data or a secret object"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod (#15277)
* Starter PKI CA Storage API (#14796)
* Simple starting PKI storage api for CA rotation
* Add key and issuer storage apis
* Add listKeys and listIssuers storage implementations
* Add simple keys and issuers configuration storage api methods
* Handle resolving key, issuer references
The API context will usually have a user-specified reference to the key.
This is either the literal string "default" to select the default key,
an identifier of the key, or a slug name for the key. Here, we wish to
resolve this reference to an actual identifier that can be understood by
storage.
Also adds the missing Name field to keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add method to fetch an issuer's cert bundle
This adds a method to construct a certutil.CertBundle from the specified
issuer identifier, optionally loading its corresponding key for signing.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor certutil PrivateKey PEM handling
This refactors the parsing of PrivateKeys from PEM blobs into shared
methods (ParsePEMKey, ParseDERKey) that can be reused by the existing
Bundle parsing logic (ParsePEMBundle) or independently in the new
issuers/key-based PKI storage code.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add importKey, importCert to PKI storage
importKey is generally preferable to the low-level writeKey for adding
new entries. This takes only the contents of the private key (as a
string -- so a PEM bundle or a managed key handle) and checks if it
already exists in the storage.
If it does, it returns the existing key instance.
Otherwise, we create a new one. In the process, we detect any issuers
using this key and link them back to the new key entry.
The same holds for importCert over importKey, with the note that keys
are not modified when importing certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for importing issuers, keys
This adds tests for importing keys and issuers into the new storage
layout, ensuring that identifiers are correctly inferred and linked.
Note that directly writing entries to storage (writeKey/writeissuer)
will take KeyID links from the parent entry and should not be used for
import; only existing entries should be updated with this info.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Implement PKI storage migration.
- Hook into the backend::initialize function, calling the migration on a primary only.
- Migrate an existing certificate bundle to the new issuers and key layout
* Make fetchCAInfo aware of new storage layout
This allows fetchCAInfo to fetch a specified issuer, via a reference
parameter provided by the user. We pass that into the storage layer and
have it return a cert bundle for us. Finally, we need to validate that
it truly has the key desired.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Begin /issuers API endpoints
This implements the fetch operations around issuers in the PKI Secrets
Engine. We implement the following operations:
- LIST /issuers - returns a list of known issuers' IDs and names.
- GET /issuer/:ref - returns a JSON blob with information about this
issuer.
- POST /issuer/:ref - allows configuring information about issuers,
presently just its name.
- DELETE /issuer/:ref - allows deleting the specified issuer.
- GET /issuer/:ref/{der,pem} - returns a raw API response with just
the DER (or PEM) of the issuer's certificate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add import to PKI Issuers API
This adds the two core import code paths to the API:
/issuers/import/cert and /issuers/import/bundle. The former differs from
the latter in that the latter allows the import of keys. This allows
operators to restrict importing of keys to privileged roles, while
allowing more operators permission to import additional certificates
(not used for signing, but instead for path/chain building).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-intermediate endpoint
This endpoint allows existing issuers to be used to sign intermediate
CA certificates. In the process, we've updated the existing
/root/sign-intermediate endpoint to be equivalent to a call to
/issuer/default/sign-intermediate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-self-issued endpoint
This endpoint allows existing issuers to be used to sign self-signed
certificates. In the process, we've updated the existing
/root/sign-self-issued endpoint to be equivalent to a call to
/issuer/default/sign-self-issued.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-verbatim endpoint
This endpoint allows existing issuers to be used to directly sign CSRs.
In the process, we've updated the existing /sign-verbatim endpoint to be
equivalent to a call to /issuer/:ref/sign-verbatim.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow configuration of default issuers
Using the new updateDefaultIssuerId(...) from the storage migration PR
allows for easy implementation of configuring the default issuer. We
restrict callers from setting blank defaults and setting default to
default.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix fetching default issuers
After setting a default issuer, one should be able to use the old /ca,
/ca_chain, and /cert/{ca,ca_chain} endpoints to fetch the default issuer
(and its chain). Update the fetchCertBySerial helper to no longer
support fetching the ca and prefer fetchCAInfo for that instead (as
we've already updated that to support fetching the new issuer location).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/{sign,issue}/:role
This updates the /sign and /issue endpoints, allowing them to take the
default issuer (if none is provided by a role) and adding
issuer-specific versions of them.
Note that at this point in time, the behavior isn't yet ideal (as
/sign/:role allows adding the ref=... parameter to override the default
issuer); a later change adding role-based issuer specification will fix
this incorrect behavior.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support root issuer generation
* Add support for issuer generate intermediate end-point
* Update issuer and key arguments to consistent values
- Update all new API endpoints to use the new agreed upon argument names.
- issuer_ref & key_ref to refer to existing
- issuer_name & key_name for new definitions
- Update returned values to always user issuer_id and key_id
* Add utility methods to fetch common ref and name arguments
- Add utility methods to fetch the issuer_name, issuer_ref, key_name and key_ref arguments from data fields.
- Centralize the logic to clean up these inputs and apply various validations to all of them.
* Rename common PKI backend handlers
- Use the buildPath convention for the function name instead of common...
* Move setting PKI defaults from writeCaBundle to proper import{keys,issuer} methods
- PR feedback, move setting up the default configuration references within
the import methods instead of within the writeCaBundle method. This should
now cover all use cases of us setting up the defaults properly.
* Introduce constants for issuer_ref, rename isKeyDefaultSet...
* Fix legacy PKI sign-verbatim api path
- Addresses some test failures due to an incorrect refactoring of a legacy api
path /sign-verbatim within PKI
* Use import code to handle intermediate, config/ca
The existing bundle import code will satisfy the intermediate import;
use it instead of the old ca_bundle import logic. Additionally, update
/config/ca to use the new import code as well.
While testing, a panic was discovered:
> reflect.Value.SetMapIndex: value of type string is not assignable to type pki.keyId
This was caused by returning a map with type issuerId->keyId; instead
switch to returning string->string maps so the audit log can properly
HMAC them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify error message on missing defaults
When the default issuer and key are missing (and haven't yet been
specified), we should clarify that error message.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test semantics for new changes
This makes two minor changes to the existing test suite:
1. Importing partial bundles should now succeed, where they'd
previously error.
2. fetchCertBySerial no longer handles CA certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support for deleting all keys, issuers
The old DELETE /root code must now delete all keys and issuers for
backwards compatibility. We strongly suggest calling individual delete
methods (DELETE /key/:key_ref or DELETE /issuer/:issuer_ref) instead,
for finer control.
In the process, we detect whether the deleted key/issuers was set as the
default. This will allow us to warn (from the single key/deletion issuer
code) whether or not the default was deleted (while allowing the
operation to succeed).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Introduce defaultRef constant within PKI
- Replace hardcoded "default" references with a constant to easily identify various usages.
- Use the addIssuerRefField function instead of redefining the field in various locations.
* Rework PKI test TestBackend_Root_Idempotency
- Validate that generate/root calls are no longer idempotent, but the bundle importing
does not generate new keys/issuers
- As before make sure that the delete root api resets everything
- Address a bug within the storage that we bombed when we had multiple different
key types within storage.
* Assign Name=current to migrated key and issuer
- Detail I missed from the RFC was to assign the Name field as "current" for migrated key and issuer.
* Build CRL upon PKI intermediary set-signed api called
- Add a call to buildCRL if we created an issuer within pathImportIssuers
- Augment existing FullCAChain to verify we have a proper CRL post set-signed api call
- Remove a code block writing out "ca" storage entry that is no longer used.
* Identify which certificate or key failed
When importing complex chains, we should identify in which certificate
or key the failure occurred.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI migration writes out empty migration log entry
- Since the elements of the struct were not exported we serialized an empty
migration log to disk and would re-run the migration
* Add chain-building logic to PKI issuers path
With the one-entry-per-issuer approach, CA Chains become implicitly
constructed from the pool of issuers. This roughly matches the existing
expectations from /config/ca (wherein a chain could be provided) and
/intemediate/set-signed (where a chain may be provided). However, in
both of those cases, we simply accepted a chain. Here, we need to be
able to reconstruct the chain from parts on disk.
However, with potential rotation of roots, we need to be aware of
disparate chains. Simply concating together all issuers isn't
sufficient. Thus we need to be able to parse a certificate's Issuer and
Subject field and reconstruct valid (and potentially parallel)
parent<->child mappings.
This attempts to handle roots, intermediates, cross-signed
intermediates, cross-signed roots, and rotated keys (wherein one might
not have a valid signature due to changed key material with the same
subject).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Return CA Chain when fetching issuers
This returns the CA Chain attribute of an issuer, showing its computed
chain based on other issuers in the database, when fetching a specific
issuer.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add testing for chain building
Using the issuance infrastructure, we generate new certificates (either
roots or intermediates), positing that this is roughly equivalent to
importing an external bundle (minus error handling during partial
imports). This allows us to incrementally construct complex chains,
creating reissuance cliques and cross-signing cycles.
By using ECDSA certificates, we avoid high signature verification and
key generation times.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow manual construction of issuer chain
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix handling of duplicate names
With the new issuer field (manual_chain), we can no longer err when a
name already exists: we might be updating the existing issuer (with the
same name), but changing its manual_chain field. Detect this error and
correctly handle it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for manual chain building
We break the clique, instead building these chains manually, ensuring
that the remaining chains do not change and only the modified certs
change. We then reset them (back to implicit chain building) and ensure
we get the same results as earlier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter verification of issuers PEM format
This ensures each issuer is only a single certificate entry (as
validated by count and parsing) without any trailing data.
We further ensure that each certificate PEM has leading and trailing
spaces removed with only a single trailing new line remaining.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix full chain building
Don't set the legacy IssuingCA field on the certificate bundle, as we
prefer the CAChain field over it.
Additionally, building the full chain could result in duplicate
certificates when the CAChain included the leaf certificate itself. When
building the full chain, ensure we don't include the bundle's
certificate twice.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter tests for full chain construction
We wish to ensure that each desired certificate in the chain is only
present once.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Rename PKI types to avoid constant variable name collisions
keyId -> keyID
issuerId -> issuerID
key -> keyEntry
issuer -> issuerEntry
keyConfig -> keyConfigEntry
issuerConfig -> issuerConfigEntry
* Update CRL handling for multiple issuers
When building CRLs, we've gotta make sure certs issued by that issuer
land up on that issuer's CRL and not some other CRL. If no CRL is
found (matching a cert), we'll place it on the default CRL.
However, in the event of equivalent issuers (those with the same subject
AND the same key material) -- perhaps due to reissuance -- we'll only
create a single (unified) CRL for them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching updated CRL locations
This updates fetchCertBySerial to support querying the default issuer's
CRL.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL storage location test case
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update to CRLv2 Format to copy RawIssuer
When using the older Certificate.CreateCRL(...) call, Go's x509 library
copies the parsed pkix.Name version of the CRL Issuer's Subject field.
For certain constructed CAs, this fails since pkix.Name is not suitable
for round-tripping. This also builds a CRLv1 (per RFC 5280) CRL.
In updating to the newer x509.CreateRevocationList(...) call, we can
construct the CRL in the CRLv2 format and correctly copy the issuer's
name. However, this requires holding an additional field per-CRL, the
CRLNumber field, which is required in Go's implementation of CRLv2
(though OPTIONAL in the spec). We store this on the new
LocalCRLConfigEntry object, per-CRL.
Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add comment regarding CRL non-assignment in GOTO
In previous versions of Vault, it was possible to sign an empty CRL
(when the CRL was disabled and a force-rebuild was requested). Add a
comment about this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching the specified issuer's CRL
We add a new API endpoint to fetch the specified issuer's CRL directly
(rather than the default issuer's CRL at /crl and /certs/crl). We also
add a new test to validate the CRL in a multi-root scenario and ensure
it is signed with the correct keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add new PKI key prefix to seal wrapped storage (#15126)
* Refactor common backend initialization within backend_test
- Leverage an existing helper method within the PKI backend tests to setup a PKI backend with storage.
* Add ability to read legacy cert bundle if the migration has not occurred on secondaries.
- Track the migration state forbidding an issuer/key writing api call if we have not migrated
- For operations that just need to read the CA bundle, use the same tracking variable to
switch between reading the legacy bundle or use the new key/issuer storage.
- Add an invalidation function that will listen for updates to our log path to refresh the state
on secondary clusters.
* Always write migration entry to trigger secondary clusters to wake up
- Some PR feedback and handle a case in which the primary cluster does
not have a CA bundle within storage but somehow a secondary does.
* Update CA Chain to report entire chain
This merges the ca_chain JSON field (of the /certs/ca_chain path) with
the regular certificate field, returning the root of trust always. This
also affects the non-JSON (raw) endpoints as well.
We return the default issuer's chain here, rather than all known issuers
(as that may not form a strict chain).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow explicit issuer override on roles
When a role is used to generate a certificate (such as with the sign/
and issue/ legacy paths or the legacy sign-verbatim/ paths), we prefer
that issuer to the one on the request. This allows operators to set an
issuer (other than default) for requests to be issued against,
effectively making the change no different from the users' perspective
as it is "just" a different role name.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for role-based issuer selection
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expand NotAfter limit enforcement behavior
Vault previously strictly enforced NotAfter/ttl values on certificate
requests, erring if the requested TTL extended past the NotAfter date of
the issuer. In the event of issuing an intermediate, this behavior was
ignored, instead permitting the issuance.
Users generally do not think to check their issuer's NotAfter date when
requesting a certificate; thus this behavior was generally surprising.
Per RFC 5280 however, issuers need to maintain status information
throughout the life cycle of the issued cert. If this leaf cert were to
be issued for a longer duration than the parent issuer, the CA must
still maintain revocation information past its expiration.
Thus, we add an option to the issuer to change the desired behavior:
- err, to err out,
- permit, to permit the longer NotAfter date, or
- truncate, to silently truncate the expiration to the issuer's
NotAfter date.
Since expiration of certificates in the system's trust store are not
generally validated (when validating an arbitrary leaf, e.g., during TLS
validation), permit should generally only be used in that case. However,
browsers usually validate intermediate's validity periods, and thus
truncate should likely be used (as with permit, the leaf's chain will
not validate towards the end of the issuance period).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for expanded issuance behaviors
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add warning on keyless default issuer (#15178)
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update PKI to new Operations framework (#15180)
The backend Framework has updated Callbacks (used extensively in PKI) to
become deprecated; Operations takes their place and clarifies forwarding
of requests.
We switch to the new format everywhere, updating some bad assumptions
about forwarding along the way. Anywhere writes are handled (that should
be propagated to all nodes in all clusters), we choose to forward the
request all the way up to the performance primary cluster's primary
node. This holds for issuers/keys, roles, and configs (such as CRL
config, which is globally set for all clusters despite all clusters
having their own separate CRL).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Kitography/vault 5474 rebase (#15150)
* These parts work (put in signature so that backend wouldn't break, but missing fields, desc, etc.)
* Import and Generate API calls w/ needed additions to SDK.
* make fmt
* Add Help/Sync Text, fix some of internal/exported/kms code.
* Fix PEM/DER Encoding issue.
* make fmt
* Standardize keyIdParam, keyNameParam, keyTypeParam
* Add error response if key to be deleted is in use.
* replaces all instances of "default" in code with defaultRef
* Updates from Callbacks to Operations Function with explicit forwarding.
* Fixes a panic with names not being updated everywhere.
* add a logged error in addition to warning on deleting default key.
* Normalize whitespace upon importing keys.
Authored-by: Alexander Scheel <alexander.m.scheel@gmail.com>
* Fix isKeyInUse functionality.
* Fixes tests associated with newline at end of key pem.
* Add alternative proposal PKI aliased paths (#15211)
* Add aliased path for root/rotate/:exported
This adds a user-friendly path name for generating a rotated root. We
automatically choose the name "next" for the newly generated root at
this path if it doesn't already exist.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add aliased path for intermediate/cross-sign
This allows cross-signatures to work.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add path for replacing the current root
This updates default to point to the value of the issuer with name
"next" rather than its current value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove plural issuers/ in signing paths
These paths use a single issuer and thus shouldn't include the plural
issuers/ as a path prefix, instead using the singular issuer/ path
prefix.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only warn if default issuer was imported
When the default issuer was not (re-)imported, we'd fail to find it,
causing an extraneous warning about missing keys, even though this
issuer indeed had a key.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing issuer sign/issue paths
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clean up various warnings within the PKI package (#15230)
* Rebuild CRLs on secondary performance clusters post migration and on new/updated issuers
- Hook into the backend invalidation function so that secondaries are notified of
new/updated issuer or migrations occuring on the primary cluster. Upon notification
schedule a CRL rebuild to take place upon the next process to read/update the CRL
or within the periodic function if no request comes in.
* Schedule rebuilding PKI CRLs on active nodes only
- Address an issue that we were scheduling the rebuilding of a CRL on standby
nodes, which would not be able to write to storage.
- Fix an issue with standby nodes not correctly determining that a migration previously
occurred.
* Return legacy CRL storage path when no migration has occurred.
* Handle issuer, keys locking (#15227)
* Handle locking of issuers during writes
We need a write lock around writes to ensure serialization of
modifications. We use a single lock for both issuer and key
updates, in part because certain operations (like deletion) will
potentially affect both.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing b.useLegacyBundleCaStorage guards
Several locations needed to guard against early usage of the new issuers
endpoint pre-migration.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Address PKI to properly support managed keys (#15256)
* Address codebase for managed key fixes
* Add proper public key comparison for better managed key support to importKeys
* Remove redundant public key fetching within PKI importKeys
* Correctly handle rebuilding remaining chains
When deleting a specific issuer, we might impact the chains. From a
consistency perspective, we need to ensure the remaining chains are
correct and don't refer to the since-deleted issuer, so trigger a full
rebuild here.
We don't need to call this in the delete-the-world (DELETE /root) code
path, as there shouldn't be any remaining issuers or chains to build.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL bundle on world deletion
When calling DELETE /root, we should remove the legacy CRL bundle, since
we're deleting the legacy CA issuer bundle as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove deleted issuers' CRL entries
Since CRLs are no longer resolvable after deletion (due to missing
issuer ID, which will cause resolution to fail regardless of if an ID or
a name/default reference was used), we should delete these CRLs from
storage to avoid leaking them.
In the event that this issuer comes back (with key material), we can
simply rebuild the CRL at that time (from the remaining revoked storage
entries).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthed JSON fetching of CRLs, Issuers (#15253)
Default to fetching JSON CRL for consistency
This makes the bare issuer-specific CRL fetching endpoint return the
JSON-wrapped CRL by default, moving the DER CRL to a specific endpoint.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add JSON-specific endpoint for fetching issuers
Unlike the unqualified /issuer/:ref endpoint (which also returns JSON),
we have a separate /issuer/:ref/json endpoint to return _only_ the
PEM-encoded certificate and the chain, mirroring the existing /cert/ca
endpoint but for a specific issuer. This allows us to make the endpoint
unauthenticated, whereas the bare endpoint would remain authenticated
and usually privileged.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add tests for raw JSON endpoints
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthenticated issuers endpoints to PKI table
This adds the unauthenticated issuers endpoints?
- LIST /issuers,
- Fetching _just_ the issuer certificates (in JSON/DER/PEM form), and
- Fetching the CRL of this issuer (in JSON/DER/PEM form).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add issuer usage restrictions bitset
This allows issuers to have usage restrictions, limiting whether they
can be used to issue certificates or if they can generate CRLs. This
allows certain issuers to not generate a CRL (if the global config is
with the CRL enabled) or allows the issuer to not issue new certificates
(but potentially letting the CRL generation continue).
Setting both fields to false effectively forms a soft delete capability.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI Pod rotation Add Base Changelog (#15283)
* PKI Pod rotation changelog.
* Use feature release-note formatting of changelog.
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
Co-authored-by: Kit Haines <kit.haines@hashicorp.com>
Co-authored-by: kitography <khaines@mit.edu>
2022-05-11 16:42:28 +00:00
|
|
|
func ParseDERKey(privateKeyBytes []byte) (signer crypto.Signer, format BlockType, err error) {
|
|
|
|
if signer, err = x509.ParseECPrivateKey(privateKeyBytes); err == nil {
|
|
|
|
format = ECBlock
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if signer, err = x509.ParsePKCS1PrivateKey(privateKeyBytes); err == nil {
|
|
|
|
format = PKCS1Block
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var rawKey interface{}
|
|
|
|
if rawKey, err = x509.ParsePKCS8PrivateKey(privateKeyBytes); err == nil {
|
|
|
|
switch rawSigner := rawKey.(type) {
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
signer = rawSigner
|
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
signer = rawSigner
|
|
|
|
case ed25519.PrivateKey:
|
|
|
|
signer = rawSigner
|
|
|
|
default:
|
|
|
|
return nil, UnknownBlock, errutil.InternalError{Err: "unknown type for parsed PKCS8 Private Key"}
|
|
|
|
}
|
|
|
|
|
|
|
|
format = PKCS8Block
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, UnknownBlock, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParsePEMKey(keyPem string) (crypto.Signer, BlockType, error) {
|
|
|
|
pemBlock, _ := pem.Decode([]byte(keyPem))
|
|
|
|
if pemBlock == nil {
|
|
|
|
return nil, UnknownBlock, errutil.UserError{Err: "no data found in PEM block"}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ParseDERKey(pemBlock.Bytes)
|
|
|
|
}
|
|
|
|
|
2015-06-17 16:43:36 +00:00
|
|
|
// ParsePEMBundle takes a string of concatenated PEM-format certificate
|
|
|
|
// and private key values and decodes/parses them, checking validity along
|
2018-01-03 15:59:18 +00:00
|
|
|
// the way. The first certificate must be the subject certificate and issuing
|
|
|
|
// certificates may follow. There must be at most one private key.
|
2015-06-17 16:43:36 +00:00
|
|
|
func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) {
|
|
|
|
if len(pemBundle) == 0 {
|
2018-02-05 19:26:31 +00:00
|
|
|
return nil, errutil.UserError{Err: "empty pem bundle"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pemBytes := []byte(pemBundle)
|
|
|
|
var pemBlock *pem.Block
|
|
|
|
parsedBundle := &ParsedCertBundle{}
|
2016-09-28 00:50:17 +00:00
|
|
|
var certPath []*CertBlock
|
2015-06-17 16:43:36 +00:00
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
for len(pemBytes) > 0 {
|
2015-06-17 16:43:36 +00:00
|
|
|
pemBlock, pemBytes = pem.Decode(pemBytes)
|
|
|
|
if pemBlock == nil {
|
2018-02-20 05:03:45 +00:00
|
|
|
return nil, errutil.UserError{Err: "no data found in PEM block"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod (#15277)
* Starter PKI CA Storage API (#14796)
* Simple starting PKI storage api for CA rotation
* Add key and issuer storage apis
* Add listKeys and listIssuers storage implementations
* Add simple keys and issuers configuration storage api methods
* Handle resolving key, issuer references
The API context will usually have a user-specified reference to the key.
This is either the literal string "default" to select the default key,
an identifier of the key, or a slug name for the key. Here, we wish to
resolve this reference to an actual identifier that can be understood by
storage.
Also adds the missing Name field to keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add method to fetch an issuer's cert bundle
This adds a method to construct a certutil.CertBundle from the specified
issuer identifier, optionally loading its corresponding key for signing.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor certutil PrivateKey PEM handling
This refactors the parsing of PrivateKeys from PEM blobs into shared
methods (ParsePEMKey, ParseDERKey) that can be reused by the existing
Bundle parsing logic (ParsePEMBundle) or independently in the new
issuers/key-based PKI storage code.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add importKey, importCert to PKI storage
importKey is generally preferable to the low-level writeKey for adding
new entries. This takes only the contents of the private key (as a
string -- so a PEM bundle or a managed key handle) and checks if it
already exists in the storage.
If it does, it returns the existing key instance.
Otherwise, we create a new one. In the process, we detect any issuers
using this key and link them back to the new key entry.
The same holds for importCert over importKey, with the note that keys
are not modified when importing certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for importing issuers, keys
This adds tests for importing keys and issuers into the new storage
layout, ensuring that identifiers are correctly inferred and linked.
Note that directly writing entries to storage (writeKey/writeissuer)
will take KeyID links from the parent entry and should not be used for
import; only existing entries should be updated with this info.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Implement PKI storage migration.
- Hook into the backend::initialize function, calling the migration on a primary only.
- Migrate an existing certificate bundle to the new issuers and key layout
* Make fetchCAInfo aware of new storage layout
This allows fetchCAInfo to fetch a specified issuer, via a reference
parameter provided by the user. We pass that into the storage layer and
have it return a cert bundle for us. Finally, we need to validate that
it truly has the key desired.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Begin /issuers API endpoints
This implements the fetch operations around issuers in the PKI Secrets
Engine. We implement the following operations:
- LIST /issuers - returns a list of known issuers' IDs and names.
- GET /issuer/:ref - returns a JSON blob with information about this
issuer.
- POST /issuer/:ref - allows configuring information about issuers,
presently just its name.
- DELETE /issuer/:ref - allows deleting the specified issuer.
- GET /issuer/:ref/{der,pem} - returns a raw API response with just
the DER (or PEM) of the issuer's certificate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add import to PKI Issuers API
This adds the two core import code paths to the API:
/issuers/import/cert and /issuers/import/bundle. The former differs from
the latter in that the latter allows the import of keys. This allows
operators to restrict importing of keys to privileged roles, while
allowing more operators permission to import additional certificates
(not used for signing, but instead for path/chain building).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-intermediate endpoint
This endpoint allows existing issuers to be used to sign intermediate
CA certificates. In the process, we've updated the existing
/root/sign-intermediate endpoint to be equivalent to a call to
/issuer/default/sign-intermediate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-self-issued endpoint
This endpoint allows existing issuers to be used to sign self-signed
certificates. In the process, we've updated the existing
/root/sign-self-issued endpoint to be equivalent to a call to
/issuer/default/sign-self-issued.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-verbatim endpoint
This endpoint allows existing issuers to be used to directly sign CSRs.
In the process, we've updated the existing /sign-verbatim endpoint to be
equivalent to a call to /issuer/:ref/sign-verbatim.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow configuration of default issuers
Using the new updateDefaultIssuerId(...) from the storage migration PR
allows for easy implementation of configuring the default issuer. We
restrict callers from setting blank defaults and setting default to
default.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix fetching default issuers
After setting a default issuer, one should be able to use the old /ca,
/ca_chain, and /cert/{ca,ca_chain} endpoints to fetch the default issuer
(and its chain). Update the fetchCertBySerial helper to no longer
support fetching the ca and prefer fetchCAInfo for that instead (as
we've already updated that to support fetching the new issuer location).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/{sign,issue}/:role
This updates the /sign and /issue endpoints, allowing them to take the
default issuer (if none is provided by a role) and adding
issuer-specific versions of them.
Note that at this point in time, the behavior isn't yet ideal (as
/sign/:role allows adding the ref=... parameter to override the default
issuer); a later change adding role-based issuer specification will fix
this incorrect behavior.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support root issuer generation
* Add support for issuer generate intermediate end-point
* Update issuer and key arguments to consistent values
- Update all new API endpoints to use the new agreed upon argument names.
- issuer_ref & key_ref to refer to existing
- issuer_name & key_name for new definitions
- Update returned values to always user issuer_id and key_id
* Add utility methods to fetch common ref and name arguments
- Add utility methods to fetch the issuer_name, issuer_ref, key_name and key_ref arguments from data fields.
- Centralize the logic to clean up these inputs and apply various validations to all of them.
* Rename common PKI backend handlers
- Use the buildPath convention for the function name instead of common...
* Move setting PKI defaults from writeCaBundle to proper import{keys,issuer} methods
- PR feedback, move setting up the default configuration references within
the import methods instead of within the writeCaBundle method. This should
now cover all use cases of us setting up the defaults properly.
* Introduce constants for issuer_ref, rename isKeyDefaultSet...
* Fix legacy PKI sign-verbatim api path
- Addresses some test failures due to an incorrect refactoring of a legacy api
path /sign-verbatim within PKI
* Use import code to handle intermediate, config/ca
The existing bundle import code will satisfy the intermediate import;
use it instead of the old ca_bundle import logic. Additionally, update
/config/ca to use the new import code as well.
While testing, a panic was discovered:
> reflect.Value.SetMapIndex: value of type string is not assignable to type pki.keyId
This was caused by returning a map with type issuerId->keyId; instead
switch to returning string->string maps so the audit log can properly
HMAC them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify error message on missing defaults
When the default issuer and key are missing (and haven't yet been
specified), we should clarify that error message.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test semantics for new changes
This makes two minor changes to the existing test suite:
1. Importing partial bundles should now succeed, where they'd
previously error.
2. fetchCertBySerial no longer handles CA certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support for deleting all keys, issuers
The old DELETE /root code must now delete all keys and issuers for
backwards compatibility. We strongly suggest calling individual delete
methods (DELETE /key/:key_ref or DELETE /issuer/:issuer_ref) instead,
for finer control.
In the process, we detect whether the deleted key/issuers was set as the
default. This will allow us to warn (from the single key/deletion issuer
code) whether or not the default was deleted (while allowing the
operation to succeed).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Introduce defaultRef constant within PKI
- Replace hardcoded "default" references with a constant to easily identify various usages.
- Use the addIssuerRefField function instead of redefining the field in various locations.
* Rework PKI test TestBackend_Root_Idempotency
- Validate that generate/root calls are no longer idempotent, but the bundle importing
does not generate new keys/issuers
- As before make sure that the delete root api resets everything
- Address a bug within the storage that we bombed when we had multiple different
key types within storage.
* Assign Name=current to migrated key and issuer
- Detail I missed from the RFC was to assign the Name field as "current" for migrated key and issuer.
* Build CRL upon PKI intermediary set-signed api called
- Add a call to buildCRL if we created an issuer within pathImportIssuers
- Augment existing FullCAChain to verify we have a proper CRL post set-signed api call
- Remove a code block writing out "ca" storage entry that is no longer used.
* Identify which certificate or key failed
When importing complex chains, we should identify in which certificate
or key the failure occurred.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI migration writes out empty migration log entry
- Since the elements of the struct were not exported we serialized an empty
migration log to disk and would re-run the migration
* Add chain-building logic to PKI issuers path
With the one-entry-per-issuer approach, CA Chains become implicitly
constructed from the pool of issuers. This roughly matches the existing
expectations from /config/ca (wherein a chain could be provided) and
/intemediate/set-signed (where a chain may be provided). However, in
both of those cases, we simply accepted a chain. Here, we need to be
able to reconstruct the chain from parts on disk.
However, with potential rotation of roots, we need to be aware of
disparate chains. Simply concating together all issuers isn't
sufficient. Thus we need to be able to parse a certificate's Issuer and
Subject field and reconstruct valid (and potentially parallel)
parent<->child mappings.
This attempts to handle roots, intermediates, cross-signed
intermediates, cross-signed roots, and rotated keys (wherein one might
not have a valid signature due to changed key material with the same
subject).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Return CA Chain when fetching issuers
This returns the CA Chain attribute of an issuer, showing its computed
chain based on other issuers in the database, when fetching a specific
issuer.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add testing for chain building
Using the issuance infrastructure, we generate new certificates (either
roots or intermediates), positing that this is roughly equivalent to
importing an external bundle (minus error handling during partial
imports). This allows us to incrementally construct complex chains,
creating reissuance cliques and cross-signing cycles.
By using ECDSA certificates, we avoid high signature verification and
key generation times.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow manual construction of issuer chain
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix handling of duplicate names
With the new issuer field (manual_chain), we can no longer err when a
name already exists: we might be updating the existing issuer (with the
same name), but changing its manual_chain field. Detect this error and
correctly handle it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for manual chain building
We break the clique, instead building these chains manually, ensuring
that the remaining chains do not change and only the modified certs
change. We then reset them (back to implicit chain building) and ensure
we get the same results as earlier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter verification of issuers PEM format
This ensures each issuer is only a single certificate entry (as
validated by count and parsing) without any trailing data.
We further ensure that each certificate PEM has leading and trailing
spaces removed with only a single trailing new line remaining.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix full chain building
Don't set the legacy IssuingCA field on the certificate bundle, as we
prefer the CAChain field over it.
Additionally, building the full chain could result in duplicate
certificates when the CAChain included the leaf certificate itself. When
building the full chain, ensure we don't include the bundle's
certificate twice.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter tests for full chain construction
We wish to ensure that each desired certificate in the chain is only
present once.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Rename PKI types to avoid constant variable name collisions
keyId -> keyID
issuerId -> issuerID
key -> keyEntry
issuer -> issuerEntry
keyConfig -> keyConfigEntry
issuerConfig -> issuerConfigEntry
* Update CRL handling for multiple issuers
When building CRLs, we've gotta make sure certs issued by that issuer
land up on that issuer's CRL and not some other CRL. If no CRL is
found (matching a cert), we'll place it on the default CRL.
However, in the event of equivalent issuers (those with the same subject
AND the same key material) -- perhaps due to reissuance -- we'll only
create a single (unified) CRL for them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching updated CRL locations
This updates fetchCertBySerial to support querying the default issuer's
CRL.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL storage location test case
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update to CRLv2 Format to copy RawIssuer
When using the older Certificate.CreateCRL(...) call, Go's x509 library
copies the parsed pkix.Name version of the CRL Issuer's Subject field.
For certain constructed CAs, this fails since pkix.Name is not suitable
for round-tripping. This also builds a CRLv1 (per RFC 5280) CRL.
In updating to the newer x509.CreateRevocationList(...) call, we can
construct the CRL in the CRLv2 format and correctly copy the issuer's
name. However, this requires holding an additional field per-CRL, the
CRLNumber field, which is required in Go's implementation of CRLv2
(though OPTIONAL in the spec). We store this on the new
LocalCRLConfigEntry object, per-CRL.
Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add comment regarding CRL non-assignment in GOTO
In previous versions of Vault, it was possible to sign an empty CRL
(when the CRL was disabled and a force-rebuild was requested). Add a
comment about this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching the specified issuer's CRL
We add a new API endpoint to fetch the specified issuer's CRL directly
(rather than the default issuer's CRL at /crl and /certs/crl). We also
add a new test to validate the CRL in a multi-root scenario and ensure
it is signed with the correct keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add new PKI key prefix to seal wrapped storage (#15126)
* Refactor common backend initialization within backend_test
- Leverage an existing helper method within the PKI backend tests to setup a PKI backend with storage.
* Add ability to read legacy cert bundle if the migration has not occurred on secondaries.
- Track the migration state forbidding an issuer/key writing api call if we have not migrated
- For operations that just need to read the CA bundle, use the same tracking variable to
switch between reading the legacy bundle or use the new key/issuer storage.
- Add an invalidation function that will listen for updates to our log path to refresh the state
on secondary clusters.
* Always write migration entry to trigger secondary clusters to wake up
- Some PR feedback and handle a case in which the primary cluster does
not have a CA bundle within storage but somehow a secondary does.
* Update CA Chain to report entire chain
This merges the ca_chain JSON field (of the /certs/ca_chain path) with
the regular certificate field, returning the root of trust always. This
also affects the non-JSON (raw) endpoints as well.
We return the default issuer's chain here, rather than all known issuers
(as that may not form a strict chain).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow explicit issuer override on roles
When a role is used to generate a certificate (such as with the sign/
and issue/ legacy paths or the legacy sign-verbatim/ paths), we prefer
that issuer to the one on the request. This allows operators to set an
issuer (other than default) for requests to be issued against,
effectively making the change no different from the users' perspective
as it is "just" a different role name.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for role-based issuer selection
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expand NotAfter limit enforcement behavior
Vault previously strictly enforced NotAfter/ttl values on certificate
requests, erring if the requested TTL extended past the NotAfter date of
the issuer. In the event of issuing an intermediate, this behavior was
ignored, instead permitting the issuance.
Users generally do not think to check their issuer's NotAfter date when
requesting a certificate; thus this behavior was generally surprising.
Per RFC 5280 however, issuers need to maintain status information
throughout the life cycle of the issued cert. If this leaf cert were to
be issued for a longer duration than the parent issuer, the CA must
still maintain revocation information past its expiration.
Thus, we add an option to the issuer to change the desired behavior:
- err, to err out,
- permit, to permit the longer NotAfter date, or
- truncate, to silently truncate the expiration to the issuer's
NotAfter date.
Since expiration of certificates in the system's trust store are not
generally validated (when validating an arbitrary leaf, e.g., during TLS
validation), permit should generally only be used in that case. However,
browsers usually validate intermediate's validity periods, and thus
truncate should likely be used (as with permit, the leaf's chain will
not validate towards the end of the issuance period).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for expanded issuance behaviors
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add warning on keyless default issuer (#15178)
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update PKI to new Operations framework (#15180)
The backend Framework has updated Callbacks (used extensively in PKI) to
become deprecated; Operations takes their place and clarifies forwarding
of requests.
We switch to the new format everywhere, updating some bad assumptions
about forwarding along the way. Anywhere writes are handled (that should
be propagated to all nodes in all clusters), we choose to forward the
request all the way up to the performance primary cluster's primary
node. This holds for issuers/keys, roles, and configs (such as CRL
config, which is globally set for all clusters despite all clusters
having their own separate CRL).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Kitography/vault 5474 rebase (#15150)
* These parts work (put in signature so that backend wouldn't break, but missing fields, desc, etc.)
* Import and Generate API calls w/ needed additions to SDK.
* make fmt
* Add Help/Sync Text, fix some of internal/exported/kms code.
* Fix PEM/DER Encoding issue.
* make fmt
* Standardize keyIdParam, keyNameParam, keyTypeParam
* Add error response if key to be deleted is in use.
* replaces all instances of "default" in code with defaultRef
* Updates from Callbacks to Operations Function with explicit forwarding.
* Fixes a panic with names not being updated everywhere.
* add a logged error in addition to warning on deleting default key.
* Normalize whitespace upon importing keys.
Authored-by: Alexander Scheel <alexander.m.scheel@gmail.com>
* Fix isKeyInUse functionality.
* Fixes tests associated with newline at end of key pem.
* Add alternative proposal PKI aliased paths (#15211)
* Add aliased path for root/rotate/:exported
This adds a user-friendly path name for generating a rotated root. We
automatically choose the name "next" for the newly generated root at
this path if it doesn't already exist.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add aliased path for intermediate/cross-sign
This allows cross-signatures to work.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add path for replacing the current root
This updates default to point to the value of the issuer with name
"next" rather than its current value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove plural issuers/ in signing paths
These paths use a single issuer and thus shouldn't include the plural
issuers/ as a path prefix, instead using the singular issuer/ path
prefix.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only warn if default issuer was imported
When the default issuer was not (re-)imported, we'd fail to find it,
causing an extraneous warning about missing keys, even though this
issuer indeed had a key.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing issuer sign/issue paths
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clean up various warnings within the PKI package (#15230)
* Rebuild CRLs on secondary performance clusters post migration and on new/updated issuers
- Hook into the backend invalidation function so that secondaries are notified of
new/updated issuer or migrations occuring on the primary cluster. Upon notification
schedule a CRL rebuild to take place upon the next process to read/update the CRL
or within the periodic function if no request comes in.
* Schedule rebuilding PKI CRLs on active nodes only
- Address an issue that we were scheduling the rebuilding of a CRL on standby
nodes, which would not be able to write to storage.
- Fix an issue with standby nodes not correctly determining that a migration previously
occurred.
* Return legacy CRL storage path when no migration has occurred.
* Handle issuer, keys locking (#15227)
* Handle locking of issuers during writes
We need a write lock around writes to ensure serialization of
modifications. We use a single lock for both issuer and key
updates, in part because certain operations (like deletion) will
potentially affect both.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing b.useLegacyBundleCaStorage guards
Several locations needed to guard against early usage of the new issuers
endpoint pre-migration.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Address PKI to properly support managed keys (#15256)
* Address codebase for managed key fixes
* Add proper public key comparison for better managed key support to importKeys
* Remove redundant public key fetching within PKI importKeys
* Correctly handle rebuilding remaining chains
When deleting a specific issuer, we might impact the chains. From a
consistency perspective, we need to ensure the remaining chains are
correct and don't refer to the since-deleted issuer, so trigger a full
rebuild here.
We don't need to call this in the delete-the-world (DELETE /root) code
path, as there shouldn't be any remaining issuers or chains to build.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL bundle on world deletion
When calling DELETE /root, we should remove the legacy CRL bundle, since
we're deleting the legacy CA issuer bundle as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove deleted issuers' CRL entries
Since CRLs are no longer resolvable after deletion (due to missing
issuer ID, which will cause resolution to fail regardless of if an ID or
a name/default reference was used), we should delete these CRLs from
storage to avoid leaking them.
In the event that this issuer comes back (with key material), we can
simply rebuild the CRL at that time (from the remaining revoked storage
entries).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthed JSON fetching of CRLs, Issuers (#15253)
Default to fetching JSON CRL for consistency
This makes the bare issuer-specific CRL fetching endpoint return the
JSON-wrapped CRL by default, moving the DER CRL to a specific endpoint.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add JSON-specific endpoint for fetching issuers
Unlike the unqualified /issuer/:ref endpoint (which also returns JSON),
we have a separate /issuer/:ref/json endpoint to return _only_ the
PEM-encoded certificate and the chain, mirroring the existing /cert/ca
endpoint but for a specific issuer. This allows us to make the endpoint
unauthenticated, whereas the bare endpoint would remain authenticated
and usually privileged.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add tests for raw JSON endpoints
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthenticated issuers endpoints to PKI table
This adds the unauthenticated issuers endpoints?
- LIST /issuers,
- Fetching _just_ the issuer certificates (in JSON/DER/PEM form), and
- Fetching the CRL of this issuer (in JSON/DER/PEM form).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add issuer usage restrictions bitset
This allows issuers to have usage restrictions, limiting whether they
can be used to issue certificates or if they can generate CRLs. This
allows certain issuers to not generate a CRL (if the global config is
with the CRL enabled) or allows the issuer to not issue new certificates
(but potentially letting the CRL generation continue).
Setting both fields to false effectively forms a soft delete capability.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI Pod rotation Add Base Changelog (#15283)
* PKI Pod rotation changelog.
* Use feature release-note formatting of changelog.
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
Co-authored-by: Kit Haines <kit.haines@hashicorp.com>
Co-authored-by: kitography <khaines@mit.edu>
2022-05-11 16:42:28 +00:00
|
|
|
if signer, format, err := ParseDERKey(pemBlock.Bytes); err == nil {
|
2015-06-17 16:43:36 +00:00
|
|
|
if parsedBundle.PrivateKeyType != UnknownPrivateKey {
|
2018-02-05 19:26:31 +00:00
|
|
|
return nil, errutil.UserError{Err: "more than one private key given; provide only one private key in the bundle"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod (#15277)
* Starter PKI CA Storage API (#14796)
* Simple starting PKI storage api for CA rotation
* Add key and issuer storage apis
* Add listKeys and listIssuers storage implementations
* Add simple keys and issuers configuration storage api methods
* Handle resolving key, issuer references
The API context will usually have a user-specified reference to the key.
This is either the literal string "default" to select the default key,
an identifier of the key, or a slug name for the key. Here, we wish to
resolve this reference to an actual identifier that can be understood by
storage.
Also adds the missing Name field to keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add method to fetch an issuer's cert bundle
This adds a method to construct a certutil.CertBundle from the specified
issuer identifier, optionally loading its corresponding key for signing.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor certutil PrivateKey PEM handling
This refactors the parsing of PrivateKeys from PEM blobs into shared
methods (ParsePEMKey, ParseDERKey) that can be reused by the existing
Bundle parsing logic (ParsePEMBundle) or independently in the new
issuers/key-based PKI storage code.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add importKey, importCert to PKI storage
importKey is generally preferable to the low-level writeKey for adding
new entries. This takes only the contents of the private key (as a
string -- so a PEM bundle or a managed key handle) and checks if it
already exists in the storage.
If it does, it returns the existing key instance.
Otherwise, we create a new one. In the process, we detect any issuers
using this key and link them back to the new key entry.
The same holds for importCert over importKey, with the note that keys
are not modified when importing certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for importing issuers, keys
This adds tests for importing keys and issuers into the new storage
layout, ensuring that identifiers are correctly inferred and linked.
Note that directly writing entries to storage (writeKey/writeissuer)
will take KeyID links from the parent entry and should not be used for
import; only existing entries should be updated with this info.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Implement PKI storage migration.
- Hook into the backend::initialize function, calling the migration on a primary only.
- Migrate an existing certificate bundle to the new issuers and key layout
* Make fetchCAInfo aware of new storage layout
This allows fetchCAInfo to fetch a specified issuer, via a reference
parameter provided by the user. We pass that into the storage layer and
have it return a cert bundle for us. Finally, we need to validate that
it truly has the key desired.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Begin /issuers API endpoints
This implements the fetch operations around issuers in the PKI Secrets
Engine. We implement the following operations:
- LIST /issuers - returns a list of known issuers' IDs and names.
- GET /issuer/:ref - returns a JSON blob with information about this
issuer.
- POST /issuer/:ref - allows configuring information about issuers,
presently just its name.
- DELETE /issuer/:ref - allows deleting the specified issuer.
- GET /issuer/:ref/{der,pem} - returns a raw API response with just
the DER (or PEM) of the issuer's certificate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add import to PKI Issuers API
This adds the two core import code paths to the API:
/issuers/import/cert and /issuers/import/bundle. The former differs from
the latter in that the latter allows the import of keys. This allows
operators to restrict importing of keys to privileged roles, while
allowing more operators permission to import additional certificates
(not used for signing, but instead for path/chain building).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-intermediate endpoint
This endpoint allows existing issuers to be used to sign intermediate
CA certificates. In the process, we've updated the existing
/root/sign-intermediate endpoint to be equivalent to a call to
/issuer/default/sign-intermediate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-self-issued endpoint
This endpoint allows existing issuers to be used to sign self-signed
certificates. In the process, we've updated the existing
/root/sign-self-issued endpoint to be equivalent to a call to
/issuer/default/sign-self-issued.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-verbatim endpoint
This endpoint allows existing issuers to be used to directly sign CSRs.
In the process, we've updated the existing /sign-verbatim endpoint to be
equivalent to a call to /issuer/:ref/sign-verbatim.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow configuration of default issuers
Using the new updateDefaultIssuerId(...) from the storage migration PR
allows for easy implementation of configuring the default issuer. We
restrict callers from setting blank defaults and setting default to
default.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix fetching default issuers
After setting a default issuer, one should be able to use the old /ca,
/ca_chain, and /cert/{ca,ca_chain} endpoints to fetch the default issuer
(and its chain). Update the fetchCertBySerial helper to no longer
support fetching the ca and prefer fetchCAInfo for that instead (as
we've already updated that to support fetching the new issuer location).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/{sign,issue}/:role
This updates the /sign and /issue endpoints, allowing them to take the
default issuer (if none is provided by a role) and adding
issuer-specific versions of them.
Note that at this point in time, the behavior isn't yet ideal (as
/sign/:role allows adding the ref=... parameter to override the default
issuer); a later change adding role-based issuer specification will fix
this incorrect behavior.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support root issuer generation
* Add support for issuer generate intermediate end-point
* Update issuer and key arguments to consistent values
- Update all new API endpoints to use the new agreed upon argument names.
- issuer_ref & key_ref to refer to existing
- issuer_name & key_name for new definitions
- Update returned values to always user issuer_id and key_id
* Add utility methods to fetch common ref and name arguments
- Add utility methods to fetch the issuer_name, issuer_ref, key_name and key_ref arguments from data fields.
- Centralize the logic to clean up these inputs and apply various validations to all of them.
* Rename common PKI backend handlers
- Use the buildPath convention for the function name instead of common...
* Move setting PKI defaults from writeCaBundle to proper import{keys,issuer} methods
- PR feedback, move setting up the default configuration references within
the import methods instead of within the writeCaBundle method. This should
now cover all use cases of us setting up the defaults properly.
* Introduce constants for issuer_ref, rename isKeyDefaultSet...
* Fix legacy PKI sign-verbatim api path
- Addresses some test failures due to an incorrect refactoring of a legacy api
path /sign-verbatim within PKI
* Use import code to handle intermediate, config/ca
The existing bundle import code will satisfy the intermediate import;
use it instead of the old ca_bundle import logic. Additionally, update
/config/ca to use the new import code as well.
While testing, a panic was discovered:
> reflect.Value.SetMapIndex: value of type string is not assignable to type pki.keyId
This was caused by returning a map with type issuerId->keyId; instead
switch to returning string->string maps so the audit log can properly
HMAC them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify error message on missing defaults
When the default issuer and key are missing (and haven't yet been
specified), we should clarify that error message.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test semantics for new changes
This makes two minor changes to the existing test suite:
1. Importing partial bundles should now succeed, where they'd
previously error.
2. fetchCertBySerial no longer handles CA certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support for deleting all keys, issuers
The old DELETE /root code must now delete all keys and issuers for
backwards compatibility. We strongly suggest calling individual delete
methods (DELETE /key/:key_ref or DELETE /issuer/:issuer_ref) instead,
for finer control.
In the process, we detect whether the deleted key/issuers was set as the
default. This will allow us to warn (from the single key/deletion issuer
code) whether or not the default was deleted (while allowing the
operation to succeed).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Introduce defaultRef constant within PKI
- Replace hardcoded "default" references with a constant to easily identify various usages.
- Use the addIssuerRefField function instead of redefining the field in various locations.
* Rework PKI test TestBackend_Root_Idempotency
- Validate that generate/root calls are no longer idempotent, but the bundle importing
does not generate new keys/issuers
- As before make sure that the delete root api resets everything
- Address a bug within the storage that we bombed when we had multiple different
key types within storage.
* Assign Name=current to migrated key and issuer
- Detail I missed from the RFC was to assign the Name field as "current" for migrated key and issuer.
* Build CRL upon PKI intermediary set-signed api called
- Add a call to buildCRL if we created an issuer within pathImportIssuers
- Augment existing FullCAChain to verify we have a proper CRL post set-signed api call
- Remove a code block writing out "ca" storage entry that is no longer used.
* Identify which certificate or key failed
When importing complex chains, we should identify in which certificate
or key the failure occurred.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI migration writes out empty migration log entry
- Since the elements of the struct were not exported we serialized an empty
migration log to disk and would re-run the migration
* Add chain-building logic to PKI issuers path
With the one-entry-per-issuer approach, CA Chains become implicitly
constructed from the pool of issuers. This roughly matches the existing
expectations from /config/ca (wherein a chain could be provided) and
/intemediate/set-signed (where a chain may be provided). However, in
both of those cases, we simply accepted a chain. Here, we need to be
able to reconstruct the chain from parts on disk.
However, with potential rotation of roots, we need to be aware of
disparate chains. Simply concating together all issuers isn't
sufficient. Thus we need to be able to parse a certificate's Issuer and
Subject field and reconstruct valid (and potentially parallel)
parent<->child mappings.
This attempts to handle roots, intermediates, cross-signed
intermediates, cross-signed roots, and rotated keys (wherein one might
not have a valid signature due to changed key material with the same
subject).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Return CA Chain when fetching issuers
This returns the CA Chain attribute of an issuer, showing its computed
chain based on other issuers in the database, when fetching a specific
issuer.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add testing for chain building
Using the issuance infrastructure, we generate new certificates (either
roots or intermediates), positing that this is roughly equivalent to
importing an external bundle (minus error handling during partial
imports). This allows us to incrementally construct complex chains,
creating reissuance cliques and cross-signing cycles.
By using ECDSA certificates, we avoid high signature verification and
key generation times.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow manual construction of issuer chain
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix handling of duplicate names
With the new issuer field (manual_chain), we can no longer err when a
name already exists: we might be updating the existing issuer (with the
same name), but changing its manual_chain field. Detect this error and
correctly handle it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for manual chain building
We break the clique, instead building these chains manually, ensuring
that the remaining chains do not change and only the modified certs
change. We then reset them (back to implicit chain building) and ensure
we get the same results as earlier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter verification of issuers PEM format
This ensures each issuer is only a single certificate entry (as
validated by count and parsing) without any trailing data.
We further ensure that each certificate PEM has leading and trailing
spaces removed with only a single trailing new line remaining.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix full chain building
Don't set the legacy IssuingCA field on the certificate bundle, as we
prefer the CAChain field over it.
Additionally, building the full chain could result in duplicate
certificates when the CAChain included the leaf certificate itself. When
building the full chain, ensure we don't include the bundle's
certificate twice.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter tests for full chain construction
We wish to ensure that each desired certificate in the chain is only
present once.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Rename PKI types to avoid constant variable name collisions
keyId -> keyID
issuerId -> issuerID
key -> keyEntry
issuer -> issuerEntry
keyConfig -> keyConfigEntry
issuerConfig -> issuerConfigEntry
* Update CRL handling for multiple issuers
When building CRLs, we've gotta make sure certs issued by that issuer
land up on that issuer's CRL and not some other CRL. If no CRL is
found (matching a cert), we'll place it on the default CRL.
However, in the event of equivalent issuers (those with the same subject
AND the same key material) -- perhaps due to reissuance -- we'll only
create a single (unified) CRL for them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching updated CRL locations
This updates fetchCertBySerial to support querying the default issuer's
CRL.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL storage location test case
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update to CRLv2 Format to copy RawIssuer
When using the older Certificate.CreateCRL(...) call, Go's x509 library
copies the parsed pkix.Name version of the CRL Issuer's Subject field.
For certain constructed CAs, this fails since pkix.Name is not suitable
for round-tripping. This also builds a CRLv1 (per RFC 5280) CRL.
In updating to the newer x509.CreateRevocationList(...) call, we can
construct the CRL in the CRLv2 format and correctly copy the issuer's
name. However, this requires holding an additional field per-CRL, the
CRLNumber field, which is required in Go's implementation of CRLv2
(though OPTIONAL in the spec). We store this on the new
LocalCRLConfigEntry object, per-CRL.
Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add comment regarding CRL non-assignment in GOTO
In previous versions of Vault, it was possible to sign an empty CRL
(when the CRL was disabled and a force-rebuild was requested). Add a
comment about this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching the specified issuer's CRL
We add a new API endpoint to fetch the specified issuer's CRL directly
(rather than the default issuer's CRL at /crl and /certs/crl). We also
add a new test to validate the CRL in a multi-root scenario and ensure
it is signed with the correct keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add new PKI key prefix to seal wrapped storage (#15126)
* Refactor common backend initialization within backend_test
- Leverage an existing helper method within the PKI backend tests to setup a PKI backend with storage.
* Add ability to read legacy cert bundle if the migration has not occurred on secondaries.
- Track the migration state forbidding an issuer/key writing api call if we have not migrated
- For operations that just need to read the CA bundle, use the same tracking variable to
switch between reading the legacy bundle or use the new key/issuer storage.
- Add an invalidation function that will listen for updates to our log path to refresh the state
on secondary clusters.
* Always write migration entry to trigger secondary clusters to wake up
- Some PR feedback and handle a case in which the primary cluster does
not have a CA bundle within storage but somehow a secondary does.
* Update CA Chain to report entire chain
This merges the ca_chain JSON field (of the /certs/ca_chain path) with
the regular certificate field, returning the root of trust always. This
also affects the non-JSON (raw) endpoints as well.
We return the default issuer's chain here, rather than all known issuers
(as that may not form a strict chain).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow explicit issuer override on roles
When a role is used to generate a certificate (such as with the sign/
and issue/ legacy paths or the legacy sign-verbatim/ paths), we prefer
that issuer to the one on the request. This allows operators to set an
issuer (other than default) for requests to be issued against,
effectively making the change no different from the users' perspective
as it is "just" a different role name.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for role-based issuer selection
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expand NotAfter limit enforcement behavior
Vault previously strictly enforced NotAfter/ttl values on certificate
requests, erring if the requested TTL extended past the NotAfter date of
the issuer. In the event of issuing an intermediate, this behavior was
ignored, instead permitting the issuance.
Users generally do not think to check their issuer's NotAfter date when
requesting a certificate; thus this behavior was generally surprising.
Per RFC 5280 however, issuers need to maintain status information
throughout the life cycle of the issued cert. If this leaf cert were to
be issued for a longer duration than the parent issuer, the CA must
still maintain revocation information past its expiration.
Thus, we add an option to the issuer to change the desired behavior:
- err, to err out,
- permit, to permit the longer NotAfter date, or
- truncate, to silently truncate the expiration to the issuer's
NotAfter date.
Since expiration of certificates in the system's trust store are not
generally validated (when validating an arbitrary leaf, e.g., during TLS
validation), permit should generally only be used in that case. However,
browsers usually validate intermediate's validity periods, and thus
truncate should likely be used (as with permit, the leaf's chain will
not validate towards the end of the issuance period).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for expanded issuance behaviors
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add warning on keyless default issuer (#15178)
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update PKI to new Operations framework (#15180)
The backend Framework has updated Callbacks (used extensively in PKI) to
become deprecated; Operations takes their place and clarifies forwarding
of requests.
We switch to the new format everywhere, updating some bad assumptions
about forwarding along the way. Anywhere writes are handled (that should
be propagated to all nodes in all clusters), we choose to forward the
request all the way up to the performance primary cluster's primary
node. This holds for issuers/keys, roles, and configs (such as CRL
config, which is globally set for all clusters despite all clusters
having their own separate CRL).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Kitography/vault 5474 rebase (#15150)
* These parts work (put in signature so that backend wouldn't break, but missing fields, desc, etc.)
* Import and Generate API calls w/ needed additions to SDK.
* make fmt
* Add Help/Sync Text, fix some of internal/exported/kms code.
* Fix PEM/DER Encoding issue.
* make fmt
* Standardize keyIdParam, keyNameParam, keyTypeParam
* Add error response if key to be deleted is in use.
* replaces all instances of "default" in code with defaultRef
* Updates from Callbacks to Operations Function with explicit forwarding.
* Fixes a panic with names not being updated everywhere.
* add a logged error in addition to warning on deleting default key.
* Normalize whitespace upon importing keys.
Authored-by: Alexander Scheel <alexander.m.scheel@gmail.com>
* Fix isKeyInUse functionality.
* Fixes tests associated with newline at end of key pem.
* Add alternative proposal PKI aliased paths (#15211)
* Add aliased path for root/rotate/:exported
This adds a user-friendly path name for generating a rotated root. We
automatically choose the name "next" for the newly generated root at
this path if it doesn't already exist.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add aliased path for intermediate/cross-sign
This allows cross-signatures to work.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add path for replacing the current root
This updates default to point to the value of the issuer with name
"next" rather than its current value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove plural issuers/ in signing paths
These paths use a single issuer and thus shouldn't include the plural
issuers/ as a path prefix, instead using the singular issuer/ path
prefix.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only warn if default issuer was imported
When the default issuer was not (re-)imported, we'd fail to find it,
causing an extraneous warning about missing keys, even though this
issuer indeed had a key.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing issuer sign/issue paths
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clean up various warnings within the PKI package (#15230)
* Rebuild CRLs on secondary performance clusters post migration and on new/updated issuers
- Hook into the backend invalidation function so that secondaries are notified of
new/updated issuer or migrations occuring on the primary cluster. Upon notification
schedule a CRL rebuild to take place upon the next process to read/update the CRL
or within the periodic function if no request comes in.
* Schedule rebuilding PKI CRLs on active nodes only
- Address an issue that we were scheduling the rebuilding of a CRL on standby
nodes, which would not be able to write to storage.
- Fix an issue with standby nodes not correctly determining that a migration previously
occurred.
* Return legacy CRL storage path when no migration has occurred.
* Handle issuer, keys locking (#15227)
* Handle locking of issuers during writes
We need a write lock around writes to ensure serialization of
modifications. We use a single lock for both issuer and key
updates, in part because certain operations (like deletion) will
potentially affect both.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing b.useLegacyBundleCaStorage guards
Several locations needed to guard against early usage of the new issuers
endpoint pre-migration.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Address PKI to properly support managed keys (#15256)
* Address codebase for managed key fixes
* Add proper public key comparison for better managed key support to importKeys
* Remove redundant public key fetching within PKI importKeys
* Correctly handle rebuilding remaining chains
When deleting a specific issuer, we might impact the chains. From a
consistency perspective, we need to ensure the remaining chains are
correct and don't refer to the since-deleted issuer, so trigger a full
rebuild here.
We don't need to call this in the delete-the-world (DELETE /root) code
path, as there shouldn't be any remaining issuers or chains to build.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL bundle on world deletion
When calling DELETE /root, we should remove the legacy CRL bundle, since
we're deleting the legacy CA issuer bundle as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove deleted issuers' CRL entries
Since CRLs are no longer resolvable after deletion (due to missing
issuer ID, which will cause resolution to fail regardless of if an ID or
a name/default reference was used), we should delete these CRLs from
storage to avoid leaking them.
In the event that this issuer comes back (with key material), we can
simply rebuild the CRL at that time (from the remaining revoked storage
entries).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthed JSON fetching of CRLs, Issuers (#15253)
Default to fetching JSON CRL for consistency
This makes the bare issuer-specific CRL fetching endpoint return the
JSON-wrapped CRL by default, moving the DER CRL to a specific endpoint.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add JSON-specific endpoint for fetching issuers
Unlike the unqualified /issuer/:ref endpoint (which also returns JSON),
we have a separate /issuer/:ref/json endpoint to return _only_ the
PEM-encoded certificate and the chain, mirroring the existing /cert/ca
endpoint but for a specific issuer. This allows us to make the endpoint
unauthenticated, whereas the bare endpoint would remain authenticated
and usually privileged.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add tests for raw JSON endpoints
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthenticated issuers endpoints to PKI table
This adds the unauthenticated issuers endpoints?
- LIST /issuers,
- Fetching _just_ the issuer certificates (in JSON/DER/PEM form), and
- Fetching the CRL of this issuer (in JSON/DER/PEM form).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add issuer usage restrictions bitset
This allows issuers to have usage restrictions, limiting whether they
can be used to issue certificates or if they can generate CRLs. This
allows certain issuers to not generate a CRL (if the global config is
with the CRL enabled) or allows the issuer to not issue new certificates
(but potentially letting the CRL generation continue).
Setting both fields to false effectively forms a soft delete capability.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI Pod rotation Add Base Changelog (#15283)
* PKI Pod rotation changelog.
* Use feature release-note formatting of changelog.
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
Co-authored-by: Kit Haines <kit.haines@hashicorp.com>
Co-authored-by: kitography <khaines@mit.edu>
2022-05-11 16:42:28 +00:00
|
|
|
parsedBundle.PrivateKeyFormat = format
|
|
|
|
parsedBundle.PrivateKeyType = GetPrivateKeyTypeFromSigner(signer)
|
|
|
|
if parsedBundle.PrivateKeyType == UnknownPrivateKey {
|
|
|
|
return nil, errutil.UserError{Err: "Unknown type of private key included in the bundle: %v"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod (#15277)
* Starter PKI CA Storage API (#14796)
* Simple starting PKI storage api for CA rotation
* Add key and issuer storage apis
* Add listKeys and listIssuers storage implementations
* Add simple keys and issuers configuration storage api methods
* Handle resolving key, issuer references
The API context will usually have a user-specified reference to the key.
This is either the literal string "default" to select the default key,
an identifier of the key, or a slug name for the key. Here, we wish to
resolve this reference to an actual identifier that can be understood by
storage.
Also adds the missing Name field to keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add method to fetch an issuer's cert bundle
This adds a method to construct a certutil.CertBundle from the specified
issuer identifier, optionally loading its corresponding key for signing.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor certutil PrivateKey PEM handling
This refactors the parsing of PrivateKeys from PEM blobs into shared
methods (ParsePEMKey, ParseDERKey) that can be reused by the existing
Bundle parsing logic (ParsePEMBundle) or independently in the new
issuers/key-based PKI storage code.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add importKey, importCert to PKI storage
importKey is generally preferable to the low-level writeKey for adding
new entries. This takes only the contents of the private key (as a
string -- so a PEM bundle or a managed key handle) and checks if it
already exists in the storage.
If it does, it returns the existing key instance.
Otherwise, we create a new one. In the process, we detect any issuers
using this key and link them back to the new key entry.
The same holds for importCert over importKey, with the note that keys
are not modified when importing certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for importing issuers, keys
This adds tests for importing keys and issuers into the new storage
layout, ensuring that identifiers are correctly inferred and linked.
Note that directly writing entries to storage (writeKey/writeissuer)
will take KeyID links from the parent entry and should not be used for
import; only existing entries should be updated with this info.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Implement PKI storage migration.
- Hook into the backend::initialize function, calling the migration on a primary only.
- Migrate an existing certificate bundle to the new issuers and key layout
* Make fetchCAInfo aware of new storage layout
This allows fetchCAInfo to fetch a specified issuer, via a reference
parameter provided by the user. We pass that into the storage layer and
have it return a cert bundle for us. Finally, we need to validate that
it truly has the key desired.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Begin /issuers API endpoints
This implements the fetch operations around issuers in the PKI Secrets
Engine. We implement the following operations:
- LIST /issuers - returns a list of known issuers' IDs and names.
- GET /issuer/:ref - returns a JSON blob with information about this
issuer.
- POST /issuer/:ref - allows configuring information about issuers,
presently just its name.
- DELETE /issuer/:ref - allows deleting the specified issuer.
- GET /issuer/:ref/{der,pem} - returns a raw API response with just
the DER (or PEM) of the issuer's certificate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add import to PKI Issuers API
This adds the two core import code paths to the API:
/issuers/import/cert and /issuers/import/bundle. The former differs from
the latter in that the latter allows the import of keys. This allows
operators to restrict importing of keys to privileged roles, while
allowing more operators permission to import additional certificates
(not used for signing, but instead for path/chain building).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-intermediate endpoint
This endpoint allows existing issuers to be used to sign intermediate
CA certificates. In the process, we've updated the existing
/root/sign-intermediate endpoint to be equivalent to a call to
/issuer/default/sign-intermediate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-self-issued endpoint
This endpoint allows existing issuers to be used to sign self-signed
certificates. In the process, we've updated the existing
/root/sign-self-issued endpoint to be equivalent to a call to
/issuer/default/sign-self-issued.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-verbatim endpoint
This endpoint allows existing issuers to be used to directly sign CSRs.
In the process, we've updated the existing /sign-verbatim endpoint to be
equivalent to a call to /issuer/:ref/sign-verbatim.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow configuration of default issuers
Using the new updateDefaultIssuerId(...) from the storage migration PR
allows for easy implementation of configuring the default issuer. We
restrict callers from setting blank defaults and setting default to
default.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix fetching default issuers
After setting a default issuer, one should be able to use the old /ca,
/ca_chain, and /cert/{ca,ca_chain} endpoints to fetch the default issuer
(and its chain). Update the fetchCertBySerial helper to no longer
support fetching the ca and prefer fetchCAInfo for that instead (as
we've already updated that to support fetching the new issuer location).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/{sign,issue}/:role
This updates the /sign and /issue endpoints, allowing them to take the
default issuer (if none is provided by a role) and adding
issuer-specific versions of them.
Note that at this point in time, the behavior isn't yet ideal (as
/sign/:role allows adding the ref=... parameter to override the default
issuer); a later change adding role-based issuer specification will fix
this incorrect behavior.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support root issuer generation
* Add support for issuer generate intermediate end-point
* Update issuer and key arguments to consistent values
- Update all new API endpoints to use the new agreed upon argument names.
- issuer_ref & key_ref to refer to existing
- issuer_name & key_name for new definitions
- Update returned values to always user issuer_id and key_id
* Add utility methods to fetch common ref and name arguments
- Add utility methods to fetch the issuer_name, issuer_ref, key_name and key_ref arguments from data fields.
- Centralize the logic to clean up these inputs and apply various validations to all of them.
* Rename common PKI backend handlers
- Use the buildPath convention for the function name instead of common...
* Move setting PKI defaults from writeCaBundle to proper import{keys,issuer} methods
- PR feedback, move setting up the default configuration references within
the import methods instead of within the writeCaBundle method. This should
now cover all use cases of us setting up the defaults properly.
* Introduce constants for issuer_ref, rename isKeyDefaultSet...
* Fix legacy PKI sign-verbatim api path
- Addresses some test failures due to an incorrect refactoring of a legacy api
path /sign-verbatim within PKI
* Use import code to handle intermediate, config/ca
The existing bundle import code will satisfy the intermediate import;
use it instead of the old ca_bundle import logic. Additionally, update
/config/ca to use the new import code as well.
While testing, a panic was discovered:
> reflect.Value.SetMapIndex: value of type string is not assignable to type pki.keyId
This was caused by returning a map with type issuerId->keyId; instead
switch to returning string->string maps so the audit log can properly
HMAC them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify error message on missing defaults
When the default issuer and key are missing (and haven't yet been
specified), we should clarify that error message.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test semantics for new changes
This makes two minor changes to the existing test suite:
1. Importing partial bundles should now succeed, where they'd
previously error.
2. fetchCertBySerial no longer handles CA certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support for deleting all keys, issuers
The old DELETE /root code must now delete all keys and issuers for
backwards compatibility. We strongly suggest calling individual delete
methods (DELETE /key/:key_ref or DELETE /issuer/:issuer_ref) instead,
for finer control.
In the process, we detect whether the deleted key/issuers was set as the
default. This will allow us to warn (from the single key/deletion issuer
code) whether or not the default was deleted (while allowing the
operation to succeed).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Introduce defaultRef constant within PKI
- Replace hardcoded "default" references with a constant to easily identify various usages.
- Use the addIssuerRefField function instead of redefining the field in various locations.
* Rework PKI test TestBackend_Root_Idempotency
- Validate that generate/root calls are no longer idempotent, but the bundle importing
does not generate new keys/issuers
- As before make sure that the delete root api resets everything
- Address a bug within the storage that we bombed when we had multiple different
key types within storage.
* Assign Name=current to migrated key and issuer
- Detail I missed from the RFC was to assign the Name field as "current" for migrated key and issuer.
* Build CRL upon PKI intermediary set-signed api called
- Add a call to buildCRL if we created an issuer within pathImportIssuers
- Augment existing FullCAChain to verify we have a proper CRL post set-signed api call
- Remove a code block writing out "ca" storage entry that is no longer used.
* Identify which certificate or key failed
When importing complex chains, we should identify in which certificate
or key the failure occurred.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI migration writes out empty migration log entry
- Since the elements of the struct were not exported we serialized an empty
migration log to disk and would re-run the migration
* Add chain-building logic to PKI issuers path
With the one-entry-per-issuer approach, CA Chains become implicitly
constructed from the pool of issuers. This roughly matches the existing
expectations from /config/ca (wherein a chain could be provided) and
/intemediate/set-signed (where a chain may be provided). However, in
both of those cases, we simply accepted a chain. Here, we need to be
able to reconstruct the chain from parts on disk.
However, with potential rotation of roots, we need to be aware of
disparate chains. Simply concating together all issuers isn't
sufficient. Thus we need to be able to parse a certificate's Issuer and
Subject field and reconstruct valid (and potentially parallel)
parent<->child mappings.
This attempts to handle roots, intermediates, cross-signed
intermediates, cross-signed roots, and rotated keys (wherein one might
not have a valid signature due to changed key material with the same
subject).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Return CA Chain when fetching issuers
This returns the CA Chain attribute of an issuer, showing its computed
chain based on other issuers in the database, when fetching a specific
issuer.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add testing for chain building
Using the issuance infrastructure, we generate new certificates (either
roots or intermediates), positing that this is roughly equivalent to
importing an external bundle (minus error handling during partial
imports). This allows us to incrementally construct complex chains,
creating reissuance cliques and cross-signing cycles.
By using ECDSA certificates, we avoid high signature verification and
key generation times.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow manual construction of issuer chain
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix handling of duplicate names
With the new issuer field (manual_chain), we can no longer err when a
name already exists: we might be updating the existing issuer (with the
same name), but changing its manual_chain field. Detect this error and
correctly handle it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for manual chain building
We break the clique, instead building these chains manually, ensuring
that the remaining chains do not change and only the modified certs
change. We then reset them (back to implicit chain building) and ensure
we get the same results as earlier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter verification of issuers PEM format
This ensures each issuer is only a single certificate entry (as
validated by count and parsing) without any trailing data.
We further ensure that each certificate PEM has leading and trailing
spaces removed with only a single trailing new line remaining.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix full chain building
Don't set the legacy IssuingCA field on the certificate bundle, as we
prefer the CAChain field over it.
Additionally, building the full chain could result in duplicate
certificates when the CAChain included the leaf certificate itself. When
building the full chain, ensure we don't include the bundle's
certificate twice.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter tests for full chain construction
We wish to ensure that each desired certificate in the chain is only
present once.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Rename PKI types to avoid constant variable name collisions
keyId -> keyID
issuerId -> issuerID
key -> keyEntry
issuer -> issuerEntry
keyConfig -> keyConfigEntry
issuerConfig -> issuerConfigEntry
* Update CRL handling for multiple issuers
When building CRLs, we've gotta make sure certs issued by that issuer
land up on that issuer's CRL and not some other CRL. If no CRL is
found (matching a cert), we'll place it on the default CRL.
However, in the event of equivalent issuers (those with the same subject
AND the same key material) -- perhaps due to reissuance -- we'll only
create a single (unified) CRL for them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching updated CRL locations
This updates fetchCertBySerial to support querying the default issuer's
CRL.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL storage location test case
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update to CRLv2 Format to copy RawIssuer
When using the older Certificate.CreateCRL(...) call, Go's x509 library
copies the parsed pkix.Name version of the CRL Issuer's Subject field.
For certain constructed CAs, this fails since pkix.Name is not suitable
for round-tripping. This also builds a CRLv1 (per RFC 5280) CRL.
In updating to the newer x509.CreateRevocationList(...) call, we can
construct the CRL in the CRLv2 format and correctly copy the issuer's
name. However, this requires holding an additional field per-CRL, the
CRLNumber field, which is required in Go's implementation of CRLv2
(though OPTIONAL in the spec). We store this on the new
LocalCRLConfigEntry object, per-CRL.
Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add comment regarding CRL non-assignment in GOTO
In previous versions of Vault, it was possible to sign an empty CRL
(when the CRL was disabled and a force-rebuild was requested). Add a
comment about this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching the specified issuer's CRL
We add a new API endpoint to fetch the specified issuer's CRL directly
(rather than the default issuer's CRL at /crl and /certs/crl). We also
add a new test to validate the CRL in a multi-root scenario and ensure
it is signed with the correct keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add new PKI key prefix to seal wrapped storage (#15126)
* Refactor common backend initialization within backend_test
- Leverage an existing helper method within the PKI backend tests to setup a PKI backend with storage.
* Add ability to read legacy cert bundle if the migration has not occurred on secondaries.
- Track the migration state forbidding an issuer/key writing api call if we have not migrated
- For operations that just need to read the CA bundle, use the same tracking variable to
switch between reading the legacy bundle or use the new key/issuer storage.
- Add an invalidation function that will listen for updates to our log path to refresh the state
on secondary clusters.
* Always write migration entry to trigger secondary clusters to wake up
- Some PR feedback and handle a case in which the primary cluster does
not have a CA bundle within storage but somehow a secondary does.
* Update CA Chain to report entire chain
This merges the ca_chain JSON field (of the /certs/ca_chain path) with
the regular certificate field, returning the root of trust always. This
also affects the non-JSON (raw) endpoints as well.
We return the default issuer's chain here, rather than all known issuers
(as that may not form a strict chain).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow explicit issuer override on roles
When a role is used to generate a certificate (such as with the sign/
and issue/ legacy paths or the legacy sign-verbatim/ paths), we prefer
that issuer to the one on the request. This allows operators to set an
issuer (other than default) for requests to be issued against,
effectively making the change no different from the users' perspective
as it is "just" a different role name.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for role-based issuer selection
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expand NotAfter limit enforcement behavior
Vault previously strictly enforced NotAfter/ttl values on certificate
requests, erring if the requested TTL extended past the NotAfter date of
the issuer. In the event of issuing an intermediate, this behavior was
ignored, instead permitting the issuance.
Users generally do not think to check their issuer's NotAfter date when
requesting a certificate; thus this behavior was generally surprising.
Per RFC 5280 however, issuers need to maintain status information
throughout the life cycle of the issued cert. If this leaf cert were to
be issued for a longer duration than the parent issuer, the CA must
still maintain revocation information past its expiration.
Thus, we add an option to the issuer to change the desired behavior:
- err, to err out,
- permit, to permit the longer NotAfter date, or
- truncate, to silently truncate the expiration to the issuer's
NotAfter date.
Since expiration of certificates in the system's trust store are not
generally validated (when validating an arbitrary leaf, e.g., during TLS
validation), permit should generally only be used in that case. However,
browsers usually validate intermediate's validity periods, and thus
truncate should likely be used (as with permit, the leaf's chain will
not validate towards the end of the issuance period).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for expanded issuance behaviors
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add warning on keyless default issuer (#15178)
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update PKI to new Operations framework (#15180)
The backend Framework has updated Callbacks (used extensively in PKI) to
become deprecated; Operations takes their place and clarifies forwarding
of requests.
We switch to the new format everywhere, updating some bad assumptions
about forwarding along the way. Anywhere writes are handled (that should
be propagated to all nodes in all clusters), we choose to forward the
request all the way up to the performance primary cluster's primary
node. This holds for issuers/keys, roles, and configs (such as CRL
config, which is globally set for all clusters despite all clusters
having their own separate CRL).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Kitography/vault 5474 rebase (#15150)
* These parts work (put in signature so that backend wouldn't break, but missing fields, desc, etc.)
* Import and Generate API calls w/ needed additions to SDK.
* make fmt
* Add Help/Sync Text, fix some of internal/exported/kms code.
* Fix PEM/DER Encoding issue.
* make fmt
* Standardize keyIdParam, keyNameParam, keyTypeParam
* Add error response if key to be deleted is in use.
* replaces all instances of "default" in code with defaultRef
* Updates from Callbacks to Operations Function with explicit forwarding.
* Fixes a panic with names not being updated everywhere.
* add a logged error in addition to warning on deleting default key.
* Normalize whitespace upon importing keys.
Authored-by: Alexander Scheel <alexander.m.scheel@gmail.com>
* Fix isKeyInUse functionality.
* Fixes tests associated with newline at end of key pem.
* Add alternative proposal PKI aliased paths (#15211)
* Add aliased path for root/rotate/:exported
This adds a user-friendly path name for generating a rotated root. We
automatically choose the name "next" for the newly generated root at
this path if it doesn't already exist.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add aliased path for intermediate/cross-sign
This allows cross-signatures to work.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add path for replacing the current root
This updates default to point to the value of the issuer with name
"next" rather than its current value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove plural issuers/ in signing paths
These paths use a single issuer and thus shouldn't include the plural
issuers/ as a path prefix, instead using the singular issuer/ path
prefix.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only warn if default issuer was imported
When the default issuer was not (re-)imported, we'd fail to find it,
causing an extraneous warning about missing keys, even though this
issuer indeed had a key.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing issuer sign/issue paths
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clean up various warnings within the PKI package (#15230)
* Rebuild CRLs on secondary performance clusters post migration and on new/updated issuers
- Hook into the backend invalidation function so that secondaries are notified of
new/updated issuer or migrations occuring on the primary cluster. Upon notification
schedule a CRL rebuild to take place upon the next process to read/update the CRL
or within the periodic function if no request comes in.
* Schedule rebuilding PKI CRLs on active nodes only
- Address an issue that we were scheduling the rebuilding of a CRL on standby
nodes, which would not be able to write to storage.
- Fix an issue with standby nodes not correctly determining that a migration previously
occurred.
* Return legacy CRL storage path when no migration has occurred.
* Handle issuer, keys locking (#15227)
* Handle locking of issuers during writes
We need a write lock around writes to ensure serialization of
modifications. We use a single lock for both issuer and key
updates, in part because certain operations (like deletion) will
potentially affect both.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing b.useLegacyBundleCaStorage guards
Several locations needed to guard against early usage of the new issuers
endpoint pre-migration.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Address PKI to properly support managed keys (#15256)
* Address codebase for managed key fixes
* Add proper public key comparison for better managed key support to importKeys
* Remove redundant public key fetching within PKI importKeys
* Correctly handle rebuilding remaining chains
When deleting a specific issuer, we might impact the chains. From a
consistency perspective, we need to ensure the remaining chains are
correct and don't refer to the since-deleted issuer, so trigger a full
rebuild here.
We don't need to call this in the delete-the-world (DELETE /root) code
path, as there shouldn't be any remaining issuers or chains to build.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL bundle on world deletion
When calling DELETE /root, we should remove the legacy CRL bundle, since
we're deleting the legacy CA issuer bundle as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove deleted issuers' CRL entries
Since CRLs are no longer resolvable after deletion (due to missing
issuer ID, which will cause resolution to fail regardless of if an ID or
a name/default reference was used), we should delete these CRLs from
storage to avoid leaking them.
In the event that this issuer comes back (with key material), we can
simply rebuild the CRL at that time (from the remaining revoked storage
entries).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthed JSON fetching of CRLs, Issuers (#15253)
Default to fetching JSON CRL for consistency
This makes the bare issuer-specific CRL fetching endpoint return the
JSON-wrapped CRL by default, moving the DER CRL to a specific endpoint.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add JSON-specific endpoint for fetching issuers
Unlike the unqualified /issuer/:ref endpoint (which also returns JSON),
we have a separate /issuer/:ref/json endpoint to return _only_ the
PEM-encoded certificate and the chain, mirroring the existing /cert/ca
endpoint but for a specific issuer. This allows us to make the endpoint
unauthenticated, whereas the bare endpoint would remain authenticated
and usually privileged.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add tests for raw JSON endpoints
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthenticated issuers endpoints to PKI table
This adds the unauthenticated issuers endpoints?
- LIST /issuers,
- Fetching _just_ the issuer certificates (in JSON/DER/PEM form), and
- Fetching the CRL of this issuer (in JSON/DER/PEM form).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add issuer usage restrictions bitset
This allows issuers to have usage restrictions, limiting whether they
can be used to issue certificates or if they can generate CRLs. This
allows certain issuers to not generate a CRL (if the global config is
with the CRL enabled) or allows the issuer to not issue new certificates
(but potentially letting the CRL generation continue).
Setting both fields to false effectively forms a soft delete capability.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI Pod rotation Add Base Changelog (#15283)
* PKI Pod rotation changelog.
* Use feature release-note formatting of changelog.
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
Co-authored-by: Kit Haines <kit.haines@hashicorp.com>
Co-authored-by: kitography <khaines@mit.edu>
2022-05-11 16:42:28 +00:00
|
|
|
|
2015-06-17 16:43:36 +00:00
|
|
|
parsedBundle.PrivateKeyBytes = pemBlock.Bytes
|
|
|
|
parsedBundle.PrivateKey = signer
|
|
|
|
} else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil {
|
2016-09-28 00:50:17 +00:00
|
|
|
certPath = append(certPath, &CertBlock{
|
|
|
|
Certificate: certificates[0],
|
|
|
|
Bytes: pemBlock.Bytes,
|
|
|
|
})
|
2020-05-11 23:01:10 +00:00
|
|
|
} else if x509.IsEncryptedPEMBlock(pemBlock) {
|
|
|
|
return nil, errutil.UserError{Err: "Encrypted private key given; provide only decrypted private key in the bundle"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
2016-09-28 00:50:17 +00:00
|
|
|
}
|
2015-06-17 16:43:36 +00:00
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
for i, certBlock := range certPath {
|
|
|
|
if i == 0 {
|
|
|
|
parsedBundle.Certificate = certBlock.Certificate
|
|
|
|
parsedBundle.CertificateBytes = certBlock.Bytes
|
|
|
|
} else {
|
|
|
|
parsedBundle.CAChain = append(parsedBundle.CAChain, certBlock)
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
if err := parsedBundle.Verify(); err != nil {
|
|
|
|
return nil, errutil.UserError{Err: fmt.Sprintf("verification of parsed bundle failed: %s", err)}
|
|
|
|
}
|
|
|
|
|
2015-06-17 16:43:36 +00:00
|
|
|
return parsedBundle, nil
|
|
|
|
}
|
2015-11-16 21:32:49 +00:00
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
// GeneratePrivateKey generates a private key with the specified type and key bits.
|
2015-11-19 19:15:36 +00:00
|
|
|
func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyContainer) error {
|
2021-01-08 17:48:27 +00:00
|
|
|
return generatePrivateKey(keyType, keyBits, container, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GeneratePrivateKeyWithRandomSource generates a private key with the specified type and key bits.
|
|
|
|
// GeneratePrivateKeyWithRandomSource uses randomness from the entropyReader to generate the private key.
|
|
|
|
func GeneratePrivateKeyWithRandomSource(keyType string, keyBits int, container ParsedPrivateKeyContainer, entropyReader io.Reader) error {
|
|
|
|
return generatePrivateKey(keyType, keyBits, container, entropyReader)
|
|
|
|
}
|
|
|
|
|
|
|
|
// generatePrivateKey generates a private key with the specified type and key bits.
|
|
|
|
// generatePrivateKey uses randomness from the entropyReader to generate the private key.
|
|
|
|
func generatePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyContainer, entropyReader io.Reader) error {
|
2015-11-16 21:32:49 +00:00
|
|
|
var err error
|
2015-12-11 20:43:14 +00:00
|
|
|
var privateKeyType PrivateKeyType
|
2015-11-19 19:15:36 +00:00
|
|
|
var privateKeyBytes []byte
|
|
|
|
var privateKey crypto.Signer
|
2015-11-16 21:32:49 +00:00
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
var randReader io.Reader = rand.Reader
|
|
|
|
if entropyReader != nil {
|
|
|
|
randReader = entropyReader
|
|
|
|
}
|
|
|
|
|
2015-11-16 21:32:49 +00:00
|
|
|
switch keyType {
|
|
|
|
case "rsa":
|
2021-10-26 13:30:09 +00:00
|
|
|
// XXX: there is a false-positive CodeQL path here around keyBits;
|
|
|
|
// because of a default zero value in the TypeDurationSecond and
|
|
|
|
// TypeSignedDurationSecond cases of schema.DefaultOrZero(), it
|
|
|
|
// thinks it is possible to end up with < 2048 bit RSA Key here.
|
|
|
|
// While this is true for SSH keys, it isn't true for PKI keys
|
|
|
|
// due to ValidateKeyTypeLength(...) below. While we could close
|
|
|
|
// the report as a false-positive, enforcing a minimum keyBits size
|
|
|
|
// here of 2048 would ensure no other paths exist.
|
|
|
|
if keyBits < 2048 {
|
|
|
|
return errutil.InternalError{Err: fmt.Sprintf("insecure bit length for RSA private key: %d", keyBits)}
|
|
|
|
}
|
2015-11-19 19:15:36 +00:00
|
|
|
privateKeyType = RSAPrivateKey
|
2021-01-08 17:48:27 +00:00
|
|
|
privateKey, err = rsa.GenerateKey(randReader, keyBits)
|
2015-11-16 21:32:49 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return errutil.InternalError{Err: fmt.Sprintf("error generating RSA private key: %v", err)}
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
2015-11-19 19:15:36 +00:00
|
|
|
privateKeyBytes = x509.MarshalPKCS1PrivateKey(privateKey.(*rsa.PrivateKey))
|
2015-11-16 21:32:49 +00:00
|
|
|
case "ec":
|
2015-11-19 19:15:36 +00:00
|
|
|
privateKeyType = ECPrivateKey
|
2015-11-16 21:32:49 +00:00
|
|
|
var curve elliptic.Curve
|
|
|
|
switch keyBits {
|
|
|
|
case 224:
|
|
|
|
curve = elliptic.P224()
|
|
|
|
case 256:
|
|
|
|
curve = elliptic.P256()
|
|
|
|
case 384:
|
|
|
|
curve = elliptic.P384()
|
|
|
|
case 521:
|
|
|
|
curve = elliptic.P521()
|
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return errutil.UserError{Err: fmt.Sprintf("unsupported bit length for EC key: %d", keyBits)}
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
2021-01-08 17:48:27 +00:00
|
|
|
privateKey, err = ecdsa.GenerateKey(curve, randReader)
|
2015-11-16 21:32:49 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return errutil.InternalError{Err: fmt.Sprintf("error generating EC private key: %v", err)}
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
2015-11-19 19:15:36 +00:00
|
|
|
privateKeyBytes, err = x509.MarshalECPrivateKey(privateKey.(*ecdsa.PrivateKey))
|
2015-11-16 21:32:49 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return errutil.InternalError{Err: fmt.Sprintf("error marshalling EC private key: %v", err)}
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
2021-10-05 15:28:49 +00:00
|
|
|
case "ed25519":
|
|
|
|
privateKeyType = Ed25519PrivateKey
|
|
|
|
_, privateKey, err = ed25519.GenerateKey(randReader)
|
|
|
|
if err != nil {
|
|
|
|
return errutil.InternalError{Err: fmt.Sprintf("error generating ed25519 private key: %v", err)}
|
|
|
|
}
|
|
|
|
privateKeyBytes, err = x509.MarshalPKCS8PrivateKey(privateKey.(ed25519.PrivateKey))
|
|
|
|
if err != nil {
|
|
|
|
return errutil.InternalError{Err: fmt.Sprintf("error marshalling Ed25519 private key: %v", err)}
|
|
|
|
}
|
2015-11-16 21:32:49 +00:00
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return errutil.UserError{Err: fmt.Sprintf("unknown key type: %s", keyType)}
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-19 19:15:36 +00:00
|
|
|
container.SetParsedPrivateKey(privateKey, privateKeyType, privateKeyBytes)
|
2015-11-16 21:32:49 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-17 15:01:42 +00:00
|
|
|
// GenerateSerialNumber generates a serial number suitable for a certificate
|
2015-11-16 21:32:49 +00:00
|
|
|
func GenerateSerialNumber() (*big.Int, error) {
|
2021-01-08 17:48:27 +00:00
|
|
|
return generateSerialNumber(rand.Reader)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateSerialNumberWithRandomSource generates a serial number suitable
|
|
|
|
// for a certificate with custom entropy.
|
|
|
|
func GenerateSerialNumberWithRandomSource(randReader io.Reader) (*big.Int, error) {
|
|
|
|
return generateSerialNumber(randReader)
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateSerialNumber(randReader io.Reader) (*big.Int, error) {
|
|
|
|
serial, err := rand.Int(randReader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
|
2015-11-16 21:32:49 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("error generating serial number: %v", err)}
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
|
|
|
return serial, nil
|
|
|
|
}
|
2015-11-17 15:01:42 +00:00
|
|
|
|
Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod (#15277)
* Starter PKI CA Storage API (#14796)
* Simple starting PKI storage api for CA rotation
* Add key and issuer storage apis
* Add listKeys and listIssuers storage implementations
* Add simple keys and issuers configuration storage api methods
* Handle resolving key, issuer references
The API context will usually have a user-specified reference to the key.
This is either the literal string "default" to select the default key,
an identifier of the key, or a slug name for the key. Here, we wish to
resolve this reference to an actual identifier that can be understood by
storage.
Also adds the missing Name field to keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add method to fetch an issuer's cert bundle
This adds a method to construct a certutil.CertBundle from the specified
issuer identifier, optionally loading its corresponding key for signing.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor certutil PrivateKey PEM handling
This refactors the parsing of PrivateKeys from PEM blobs into shared
methods (ParsePEMKey, ParseDERKey) that can be reused by the existing
Bundle parsing logic (ParsePEMBundle) or independently in the new
issuers/key-based PKI storage code.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add importKey, importCert to PKI storage
importKey is generally preferable to the low-level writeKey for adding
new entries. This takes only the contents of the private key (as a
string -- so a PEM bundle or a managed key handle) and checks if it
already exists in the storage.
If it does, it returns the existing key instance.
Otherwise, we create a new one. In the process, we detect any issuers
using this key and link them back to the new key entry.
The same holds for importCert over importKey, with the note that keys
are not modified when importing certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for importing issuers, keys
This adds tests for importing keys and issuers into the new storage
layout, ensuring that identifiers are correctly inferred and linked.
Note that directly writing entries to storage (writeKey/writeissuer)
will take KeyID links from the parent entry and should not be used for
import; only existing entries should be updated with this info.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Implement PKI storage migration.
- Hook into the backend::initialize function, calling the migration on a primary only.
- Migrate an existing certificate bundle to the new issuers and key layout
* Make fetchCAInfo aware of new storage layout
This allows fetchCAInfo to fetch a specified issuer, via a reference
parameter provided by the user. We pass that into the storage layer and
have it return a cert bundle for us. Finally, we need to validate that
it truly has the key desired.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Begin /issuers API endpoints
This implements the fetch operations around issuers in the PKI Secrets
Engine. We implement the following operations:
- LIST /issuers - returns a list of known issuers' IDs and names.
- GET /issuer/:ref - returns a JSON blob with information about this
issuer.
- POST /issuer/:ref - allows configuring information about issuers,
presently just its name.
- DELETE /issuer/:ref - allows deleting the specified issuer.
- GET /issuer/:ref/{der,pem} - returns a raw API response with just
the DER (or PEM) of the issuer's certificate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add import to PKI Issuers API
This adds the two core import code paths to the API:
/issuers/import/cert and /issuers/import/bundle. The former differs from
the latter in that the latter allows the import of keys. This allows
operators to restrict importing of keys to privileged roles, while
allowing more operators permission to import additional certificates
(not used for signing, but instead for path/chain building).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-intermediate endpoint
This endpoint allows existing issuers to be used to sign intermediate
CA certificates. In the process, we've updated the existing
/root/sign-intermediate endpoint to be equivalent to a call to
/issuer/default/sign-intermediate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-self-issued endpoint
This endpoint allows existing issuers to be used to sign self-signed
certificates. In the process, we've updated the existing
/root/sign-self-issued endpoint to be equivalent to a call to
/issuer/default/sign-self-issued.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-verbatim endpoint
This endpoint allows existing issuers to be used to directly sign CSRs.
In the process, we've updated the existing /sign-verbatim endpoint to be
equivalent to a call to /issuer/:ref/sign-verbatim.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow configuration of default issuers
Using the new updateDefaultIssuerId(...) from the storage migration PR
allows for easy implementation of configuring the default issuer. We
restrict callers from setting blank defaults and setting default to
default.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix fetching default issuers
After setting a default issuer, one should be able to use the old /ca,
/ca_chain, and /cert/{ca,ca_chain} endpoints to fetch the default issuer
(and its chain). Update the fetchCertBySerial helper to no longer
support fetching the ca and prefer fetchCAInfo for that instead (as
we've already updated that to support fetching the new issuer location).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/{sign,issue}/:role
This updates the /sign and /issue endpoints, allowing them to take the
default issuer (if none is provided by a role) and adding
issuer-specific versions of them.
Note that at this point in time, the behavior isn't yet ideal (as
/sign/:role allows adding the ref=... parameter to override the default
issuer); a later change adding role-based issuer specification will fix
this incorrect behavior.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support root issuer generation
* Add support for issuer generate intermediate end-point
* Update issuer and key arguments to consistent values
- Update all new API endpoints to use the new agreed upon argument names.
- issuer_ref & key_ref to refer to existing
- issuer_name & key_name for new definitions
- Update returned values to always user issuer_id and key_id
* Add utility methods to fetch common ref and name arguments
- Add utility methods to fetch the issuer_name, issuer_ref, key_name and key_ref arguments from data fields.
- Centralize the logic to clean up these inputs and apply various validations to all of them.
* Rename common PKI backend handlers
- Use the buildPath convention for the function name instead of common...
* Move setting PKI defaults from writeCaBundle to proper import{keys,issuer} methods
- PR feedback, move setting up the default configuration references within
the import methods instead of within the writeCaBundle method. This should
now cover all use cases of us setting up the defaults properly.
* Introduce constants for issuer_ref, rename isKeyDefaultSet...
* Fix legacy PKI sign-verbatim api path
- Addresses some test failures due to an incorrect refactoring of a legacy api
path /sign-verbatim within PKI
* Use import code to handle intermediate, config/ca
The existing bundle import code will satisfy the intermediate import;
use it instead of the old ca_bundle import logic. Additionally, update
/config/ca to use the new import code as well.
While testing, a panic was discovered:
> reflect.Value.SetMapIndex: value of type string is not assignable to type pki.keyId
This was caused by returning a map with type issuerId->keyId; instead
switch to returning string->string maps so the audit log can properly
HMAC them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify error message on missing defaults
When the default issuer and key are missing (and haven't yet been
specified), we should clarify that error message.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test semantics for new changes
This makes two minor changes to the existing test suite:
1. Importing partial bundles should now succeed, where they'd
previously error.
2. fetchCertBySerial no longer handles CA certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support for deleting all keys, issuers
The old DELETE /root code must now delete all keys and issuers for
backwards compatibility. We strongly suggest calling individual delete
methods (DELETE /key/:key_ref or DELETE /issuer/:issuer_ref) instead,
for finer control.
In the process, we detect whether the deleted key/issuers was set as the
default. This will allow us to warn (from the single key/deletion issuer
code) whether or not the default was deleted (while allowing the
operation to succeed).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Introduce defaultRef constant within PKI
- Replace hardcoded "default" references with a constant to easily identify various usages.
- Use the addIssuerRefField function instead of redefining the field in various locations.
* Rework PKI test TestBackend_Root_Idempotency
- Validate that generate/root calls are no longer idempotent, but the bundle importing
does not generate new keys/issuers
- As before make sure that the delete root api resets everything
- Address a bug within the storage that we bombed when we had multiple different
key types within storage.
* Assign Name=current to migrated key and issuer
- Detail I missed from the RFC was to assign the Name field as "current" for migrated key and issuer.
* Build CRL upon PKI intermediary set-signed api called
- Add a call to buildCRL if we created an issuer within pathImportIssuers
- Augment existing FullCAChain to verify we have a proper CRL post set-signed api call
- Remove a code block writing out "ca" storage entry that is no longer used.
* Identify which certificate or key failed
When importing complex chains, we should identify in which certificate
or key the failure occurred.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI migration writes out empty migration log entry
- Since the elements of the struct were not exported we serialized an empty
migration log to disk and would re-run the migration
* Add chain-building logic to PKI issuers path
With the one-entry-per-issuer approach, CA Chains become implicitly
constructed from the pool of issuers. This roughly matches the existing
expectations from /config/ca (wherein a chain could be provided) and
/intemediate/set-signed (where a chain may be provided). However, in
both of those cases, we simply accepted a chain. Here, we need to be
able to reconstruct the chain from parts on disk.
However, with potential rotation of roots, we need to be aware of
disparate chains. Simply concating together all issuers isn't
sufficient. Thus we need to be able to parse a certificate's Issuer and
Subject field and reconstruct valid (and potentially parallel)
parent<->child mappings.
This attempts to handle roots, intermediates, cross-signed
intermediates, cross-signed roots, and rotated keys (wherein one might
not have a valid signature due to changed key material with the same
subject).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Return CA Chain when fetching issuers
This returns the CA Chain attribute of an issuer, showing its computed
chain based on other issuers in the database, when fetching a specific
issuer.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add testing for chain building
Using the issuance infrastructure, we generate new certificates (either
roots or intermediates), positing that this is roughly equivalent to
importing an external bundle (minus error handling during partial
imports). This allows us to incrementally construct complex chains,
creating reissuance cliques and cross-signing cycles.
By using ECDSA certificates, we avoid high signature verification and
key generation times.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow manual construction of issuer chain
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix handling of duplicate names
With the new issuer field (manual_chain), we can no longer err when a
name already exists: we might be updating the existing issuer (with the
same name), but changing its manual_chain field. Detect this error and
correctly handle it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for manual chain building
We break the clique, instead building these chains manually, ensuring
that the remaining chains do not change and only the modified certs
change. We then reset them (back to implicit chain building) and ensure
we get the same results as earlier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter verification of issuers PEM format
This ensures each issuer is only a single certificate entry (as
validated by count and parsing) without any trailing data.
We further ensure that each certificate PEM has leading and trailing
spaces removed with only a single trailing new line remaining.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix full chain building
Don't set the legacy IssuingCA field on the certificate bundle, as we
prefer the CAChain field over it.
Additionally, building the full chain could result in duplicate
certificates when the CAChain included the leaf certificate itself. When
building the full chain, ensure we don't include the bundle's
certificate twice.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter tests for full chain construction
We wish to ensure that each desired certificate in the chain is only
present once.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Rename PKI types to avoid constant variable name collisions
keyId -> keyID
issuerId -> issuerID
key -> keyEntry
issuer -> issuerEntry
keyConfig -> keyConfigEntry
issuerConfig -> issuerConfigEntry
* Update CRL handling for multiple issuers
When building CRLs, we've gotta make sure certs issued by that issuer
land up on that issuer's CRL and not some other CRL. If no CRL is
found (matching a cert), we'll place it on the default CRL.
However, in the event of equivalent issuers (those with the same subject
AND the same key material) -- perhaps due to reissuance -- we'll only
create a single (unified) CRL for them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching updated CRL locations
This updates fetchCertBySerial to support querying the default issuer's
CRL.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL storage location test case
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update to CRLv2 Format to copy RawIssuer
When using the older Certificate.CreateCRL(...) call, Go's x509 library
copies the parsed pkix.Name version of the CRL Issuer's Subject field.
For certain constructed CAs, this fails since pkix.Name is not suitable
for round-tripping. This also builds a CRLv1 (per RFC 5280) CRL.
In updating to the newer x509.CreateRevocationList(...) call, we can
construct the CRL in the CRLv2 format and correctly copy the issuer's
name. However, this requires holding an additional field per-CRL, the
CRLNumber field, which is required in Go's implementation of CRLv2
(though OPTIONAL in the spec). We store this on the new
LocalCRLConfigEntry object, per-CRL.
Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add comment regarding CRL non-assignment in GOTO
In previous versions of Vault, it was possible to sign an empty CRL
(when the CRL was disabled and a force-rebuild was requested). Add a
comment about this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching the specified issuer's CRL
We add a new API endpoint to fetch the specified issuer's CRL directly
(rather than the default issuer's CRL at /crl and /certs/crl). We also
add a new test to validate the CRL in a multi-root scenario and ensure
it is signed with the correct keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add new PKI key prefix to seal wrapped storage (#15126)
* Refactor common backend initialization within backend_test
- Leverage an existing helper method within the PKI backend tests to setup a PKI backend with storage.
* Add ability to read legacy cert bundle if the migration has not occurred on secondaries.
- Track the migration state forbidding an issuer/key writing api call if we have not migrated
- For operations that just need to read the CA bundle, use the same tracking variable to
switch between reading the legacy bundle or use the new key/issuer storage.
- Add an invalidation function that will listen for updates to our log path to refresh the state
on secondary clusters.
* Always write migration entry to trigger secondary clusters to wake up
- Some PR feedback and handle a case in which the primary cluster does
not have a CA bundle within storage but somehow a secondary does.
* Update CA Chain to report entire chain
This merges the ca_chain JSON field (of the /certs/ca_chain path) with
the regular certificate field, returning the root of trust always. This
also affects the non-JSON (raw) endpoints as well.
We return the default issuer's chain here, rather than all known issuers
(as that may not form a strict chain).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow explicit issuer override on roles
When a role is used to generate a certificate (such as with the sign/
and issue/ legacy paths or the legacy sign-verbatim/ paths), we prefer
that issuer to the one on the request. This allows operators to set an
issuer (other than default) for requests to be issued against,
effectively making the change no different from the users' perspective
as it is "just" a different role name.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for role-based issuer selection
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expand NotAfter limit enforcement behavior
Vault previously strictly enforced NotAfter/ttl values on certificate
requests, erring if the requested TTL extended past the NotAfter date of
the issuer. In the event of issuing an intermediate, this behavior was
ignored, instead permitting the issuance.
Users generally do not think to check their issuer's NotAfter date when
requesting a certificate; thus this behavior was generally surprising.
Per RFC 5280 however, issuers need to maintain status information
throughout the life cycle of the issued cert. If this leaf cert were to
be issued for a longer duration than the parent issuer, the CA must
still maintain revocation information past its expiration.
Thus, we add an option to the issuer to change the desired behavior:
- err, to err out,
- permit, to permit the longer NotAfter date, or
- truncate, to silently truncate the expiration to the issuer's
NotAfter date.
Since expiration of certificates in the system's trust store are not
generally validated (when validating an arbitrary leaf, e.g., during TLS
validation), permit should generally only be used in that case. However,
browsers usually validate intermediate's validity periods, and thus
truncate should likely be used (as with permit, the leaf's chain will
not validate towards the end of the issuance period).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for expanded issuance behaviors
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add warning on keyless default issuer (#15178)
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update PKI to new Operations framework (#15180)
The backend Framework has updated Callbacks (used extensively in PKI) to
become deprecated; Operations takes their place and clarifies forwarding
of requests.
We switch to the new format everywhere, updating some bad assumptions
about forwarding along the way. Anywhere writes are handled (that should
be propagated to all nodes in all clusters), we choose to forward the
request all the way up to the performance primary cluster's primary
node. This holds for issuers/keys, roles, and configs (such as CRL
config, which is globally set for all clusters despite all clusters
having their own separate CRL).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Kitography/vault 5474 rebase (#15150)
* These parts work (put in signature so that backend wouldn't break, but missing fields, desc, etc.)
* Import and Generate API calls w/ needed additions to SDK.
* make fmt
* Add Help/Sync Text, fix some of internal/exported/kms code.
* Fix PEM/DER Encoding issue.
* make fmt
* Standardize keyIdParam, keyNameParam, keyTypeParam
* Add error response if key to be deleted is in use.
* replaces all instances of "default" in code with defaultRef
* Updates from Callbacks to Operations Function with explicit forwarding.
* Fixes a panic with names not being updated everywhere.
* add a logged error in addition to warning on deleting default key.
* Normalize whitespace upon importing keys.
Authored-by: Alexander Scheel <alexander.m.scheel@gmail.com>
* Fix isKeyInUse functionality.
* Fixes tests associated with newline at end of key pem.
* Add alternative proposal PKI aliased paths (#15211)
* Add aliased path for root/rotate/:exported
This adds a user-friendly path name for generating a rotated root. We
automatically choose the name "next" for the newly generated root at
this path if it doesn't already exist.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add aliased path for intermediate/cross-sign
This allows cross-signatures to work.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add path for replacing the current root
This updates default to point to the value of the issuer with name
"next" rather than its current value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove plural issuers/ in signing paths
These paths use a single issuer and thus shouldn't include the plural
issuers/ as a path prefix, instead using the singular issuer/ path
prefix.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only warn if default issuer was imported
When the default issuer was not (re-)imported, we'd fail to find it,
causing an extraneous warning about missing keys, even though this
issuer indeed had a key.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing issuer sign/issue paths
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clean up various warnings within the PKI package (#15230)
* Rebuild CRLs on secondary performance clusters post migration and on new/updated issuers
- Hook into the backend invalidation function so that secondaries are notified of
new/updated issuer or migrations occuring on the primary cluster. Upon notification
schedule a CRL rebuild to take place upon the next process to read/update the CRL
or within the periodic function if no request comes in.
* Schedule rebuilding PKI CRLs on active nodes only
- Address an issue that we were scheduling the rebuilding of a CRL on standby
nodes, which would not be able to write to storage.
- Fix an issue with standby nodes not correctly determining that a migration previously
occurred.
* Return legacy CRL storage path when no migration has occurred.
* Handle issuer, keys locking (#15227)
* Handle locking of issuers during writes
We need a write lock around writes to ensure serialization of
modifications. We use a single lock for both issuer and key
updates, in part because certain operations (like deletion) will
potentially affect both.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing b.useLegacyBundleCaStorage guards
Several locations needed to guard against early usage of the new issuers
endpoint pre-migration.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Address PKI to properly support managed keys (#15256)
* Address codebase for managed key fixes
* Add proper public key comparison for better managed key support to importKeys
* Remove redundant public key fetching within PKI importKeys
* Correctly handle rebuilding remaining chains
When deleting a specific issuer, we might impact the chains. From a
consistency perspective, we need to ensure the remaining chains are
correct and don't refer to the since-deleted issuer, so trigger a full
rebuild here.
We don't need to call this in the delete-the-world (DELETE /root) code
path, as there shouldn't be any remaining issuers or chains to build.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL bundle on world deletion
When calling DELETE /root, we should remove the legacy CRL bundle, since
we're deleting the legacy CA issuer bundle as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove deleted issuers' CRL entries
Since CRLs are no longer resolvable after deletion (due to missing
issuer ID, which will cause resolution to fail regardless of if an ID or
a name/default reference was used), we should delete these CRLs from
storage to avoid leaking them.
In the event that this issuer comes back (with key material), we can
simply rebuild the CRL at that time (from the remaining revoked storage
entries).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthed JSON fetching of CRLs, Issuers (#15253)
Default to fetching JSON CRL for consistency
This makes the bare issuer-specific CRL fetching endpoint return the
JSON-wrapped CRL by default, moving the DER CRL to a specific endpoint.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add JSON-specific endpoint for fetching issuers
Unlike the unqualified /issuer/:ref endpoint (which also returns JSON),
we have a separate /issuer/:ref/json endpoint to return _only_ the
PEM-encoded certificate and the chain, mirroring the existing /cert/ca
endpoint but for a specific issuer. This allows us to make the endpoint
unauthenticated, whereas the bare endpoint would remain authenticated
and usually privileged.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add tests for raw JSON endpoints
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthenticated issuers endpoints to PKI table
This adds the unauthenticated issuers endpoints?
- LIST /issuers,
- Fetching _just_ the issuer certificates (in JSON/DER/PEM form), and
- Fetching the CRL of this issuer (in JSON/DER/PEM form).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add issuer usage restrictions bitset
This allows issuers to have usage restrictions, limiting whether they
can be used to issue certificates or if they can generate CRLs. This
allows certain issuers to not generate a CRL (if the global config is
with the CRL enabled) or allows the issuer to not issue new certificates
(but potentially letting the CRL generation continue).
Setting both fields to false effectively forms a soft delete capability.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI Pod rotation Add Base Changelog (#15283)
* PKI Pod rotation changelog.
* Use feature release-note formatting of changelog.
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
Co-authored-by: Kit Haines <kit.haines@hashicorp.com>
Co-authored-by: kitography <khaines@mit.edu>
2022-05-11 16:42:28 +00:00
|
|
|
// ComparePublicKeysAndType compares two public keys and returns true if they match,
|
|
|
|
// false if their types or contents differ, and an error on unsupported key types.
|
|
|
|
func ComparePublicKeysAndType(key1Iface, key2Iface crypto.PublicKey) (bool, error) {
|
|
|
|
equal, err := ComparePublicKeys(key1Iface, key2Iface)
|
|
|
|
if err != nil {
|
|
|
|
if strings.Contains(err.Error(), "key types do not match:") {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return equal, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// ComparePublicKeys compares two public keys and returns true if they match,
|
|
|
|
// returns an error if public key types are mismatched, or they are an unsupported key type.
|
2015-11-17 15:01:42 +00:00
|
|
|
func ComparePublicKeys(key1Iface, key2Iface crypto.PublicKey) (bool, error) {
|
|
|
|
switch key1Iface.(type) {
|
|
|
|
case *rsa.PublicKey:
|
|
|
|
key1 := key1Iface.(*rsa.PublicKey)
|
|
|
|
key2, ok := key2Iface.(*rsa.PublicKey)
|
|
|
|
if !ok {
|
|
|
|
return false, fmt.Errorf("key types do not match: %T and %T", key1Iface, key2Iface)
|
|
|
|
}
|
|
|
|
if key1.N.Cmp(key2.N) != 0 ||
|
|
|
|
key1.E != key2.E {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
key1 := key1Iface.(*ecdsa.PublicKey)
|
|
|
|
key2, ok := key2Iface.(*ecdsa.PublicKey)
|
|
|
|
if !ok {
|
|
|
|
return false, fmt.Errorf("key types do not match: %T and %T", key1Iface, key2Iface)
|
|
|
|
}
|
|
|
|
if key1.X.Cmp(key2.X) != 0 ||
|
|
|
|
key1.Y.Cmp(key2.Y) != 0 {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
key1Params := key1.Params()
|
|
|
|
key2Params := key2.Params()
|
|
|
|
if key1Params.P.Cmp(key2Params.P) != 0 ||
|
|
|
|
key1Params.N.Cmp(key2Params.N) != 0 ||
|
|
|
|
key1Params.B.Cmp(key2Params.B) != 0 ||
|
|
|
|
key1Params.Gx.Cmp(key2Params.Gx) != 0 ||
|
|
|
|
key1Params.Gy.Cmp(key2Params.Gy) != 0 ||
|
|
|
|
key1Params.BitSize != key2Params.BitSize {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return true, nil
|
2021-10-05 15:28:49 +00:00
|
|
|
case ed25519.PublicKey:
|
|
|
|
key1 := key1Iface.(ed25519.PublicKey)
|
|
|
|
key2, ok := key2Iface.(ed25519.PublicKey)
|
|
|
|
if !ok {
|
|
|
|
return false, fmt.Errorf("key types do not match: %T and %T", key1Iface, key2Iface)
|
|
|
|
}
|
|
|
|
if !key1.Equal(key2) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return true, nil
|
2015-11-17 15:01:42 +00:00
|
|
|
default:
|
|
|
|
return false, fmt.Errorf("cannot compare key with type %T", key1Iface)
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 14:25:32 +00:00
|
|
|
|
2019-05-09 15:43:11 +00:00
|
|
|
// ParsePublicKeyPEM is used to parse RSA and ECDSA public keys from PEMs
|
2018-07-06 14:25:32 +00:00
|
|
|
func ParsePublicKeyPEM(data []byte) (interface{}, error) {
|
|
|
|
block, data := pem.Decode(data)
|
|
|
|
if block != nil {
|
2022-01-14 23:35:27 +00:00
|
|
|
if len(bytes.TrimSpace(data)) > 0 {
|
|
|
|
return nil, errutil.UserError{Err: "unexpected trailing data after parsed PEM block"}
|
|
|
|
}
|
2018-07-06 14:25:32 +00:00
|
|
|
var rawKey interface{}
|
|
|
|
var err error
|
|
|
|
if rawKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
|
|
|
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
|
|
|
rawKey = cert.PublicKey
|
|
|
|
} else {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 23:35:27 +00:00
|
|
|
switch key := rawKey.(type) {
|
|
|
|
case *rsa.PublicKey:
|
|
|
|
return key, nil
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
return key, nil
|
|
|
|
case ed25519.PublicKey:
|
|
|
|
return key, nil
|
2020-08-11 05:14:17 +00:00
|
|
|
}
|
2018-07-06 14:25:32 +00:00
|
|
|
}
|
2020-08-11 05:14:17 +00:00
|
|
|
return nil, errors.New("data does not contain any valid public keys")
|
2018-07-06 14:25:32 +00:00
|
|
|
}
|
2019-05-09 15:43:11 +00:00
|
|
|
|
2022-06-03 18:50:46 +00:00
|
|
|
// AddPolicyIdentifiers adds certificate policies extension, based on CreationBundle
|
2019-05-09 15:43:11 +00:00
|
|
|
func AddPolicyIdentifiers(data *CreationBundle, certTemplate *x509.Certificate) {
|
2022-06-03 18:50:46 +00:00
|
|
|
oidOnly := true
|
|
|
|
for _, oidStr := range data.Params.PolicyIdentifiers {
|
|
|
|
oid, err := StringToOid(oidStr)
|
2019-05-09 15:43:11 +00:00
|
|
|
if err == nil {
|
|
|
|
certTemplate.PolicyIdentifiers = append(certTemplate.PolicyIdentifiers, oid)
|
|
|
|
}
|
2022-06-03 18:50:46 +00:00
|
|
|
if err != nil {
|
|
|
|
oidOnly = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !oidOnly { // Because all policy information is held in the same extension, when we use an extra extension to
|
|
|
|
// add policy qualifier information, that overwrites any information in the PolicyIdentifiers field on the Cert
|
|
|
|
// Template, so we need to reparse all the policy identifiers here
|
|
|
|
extension, err := CreatePolicyInformationExtensionFromStorageStrings(data.Params.PolicyIdentifiers)
|
|
|
|
if err == nil {
|
|
|
|
// If this errors out, don't add it, rely on the OIDs parsed into PolicyIdentifiers above
|
|
|
|
certTemplate.ExtraExtensions = append(certTemplate.ExtraExtensions, *extension)
|
|
|
|
}
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-03 18:50:46 +00:00
|
|
|
// AddExtKeyUsageOids adds custom extended key usage OIDs to certificate
|
2019-05-09 15:43:11 +00:00
|
|
|
func AddExtKeyUsageOids(data *CreationBundle, certTemplate *x509.Certificate) {
|
|
|
|
for _, oidstr := range data.Params.ExtKeyUsageOIDs {
|
|
|
|
oid, err := StringToOid(oidstr)
|
|
|
|
if err == nil {
|
|
|
|
certTemplate.UnknownExtKeyUsage = append(certTemplate.UnknownExtKeyUsage, oid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func HandleOtherCSRSANs(in *x509.CertificateRequest, sans map[string][]string) error {
|
|
|
|
certTemplate := &x509.Certificate{
|
|
|
|
DNSNames: in.DNSNames,
|
|
|
|
IPAddresses: in.IPAddresses,
|
|
|
|
EmailAddresses: in.EmailAddresses,
|
|
|
|
URIs: in.URIs,
|
|
|
|
}
|
|
|
|
if err := HandleOtherSANs(certTemplate, sans); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(certTemplate.ExtraExtensions) > 0 {
|
|
|
|
for _, v := range certTemplate.ExtraExtensions {
|
|
|
|
in.ExtraExtensions = append(in.ExtraExtensions, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func HandleOtherSANs(in *x509.Certificate, sans map[string][]string) error {
|
|
|
|
// If other SANs is empty we return which causes normal Go stdlib parsing
|
|
|
|
// of the other SAN types
|
|
|
|
if len(sans) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var rawValues []asn1.RawValue
|
|
|
|
|
|
|
|
// We need to generate an IMPLICIT sequence for compatibility with OpenSSL
|
|
|
|
// -- it's an open question what the default for RFC 5280 actually is, see
|
|
|
|
// https://github.com/openssl/openssl/issues/5091 -- so we have to use
|
|
|
|
// cryptobyte because using the asn1 package's marshaling always produces
|
|
|
|
// an EXPLICIT sequence. Note that asn1 is way too magical according to
|
|
|
|
// agl, and cryptobyte is modeled after the CBB/CBS bits that agl put into
|
|
|
|
// boringssl.
|
|
|
|
for oid, vals := range sans {
|
|
|
|
for _, val := range vals {
|
|
|
|
var b cryptobyte.Builder
|
|
|
|
oidStr, err := StringToOid(oid)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
b.AddASN1ObjectIdentifier(oidStr)
|
|
|
|
b.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
|
|
|
|
b.AddASN1(cbasn1.UTF8String, func(b *cryptobyte.Builder) {
|
|
|
|
b.AddBytes([]byte(val))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
m, err := b.Bytes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rawValues = append(rawValues, asn1.RawValue{Tag: 0, Class: 2, IsCompound: true, Bytes: m})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If other SANs is empty we return which causes normal Go stdlib parsing
|
|
|
|
// of the other SAN types
|
|
|
|
if len(rawValues) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append any existing SANs, sans marshalling
|
|
|
|
rawValues = append(rawValues, marshalSANs(in.DNSNames, in.EmailAddresses, in.IPAddresses, in.URIs)...)
|
|
|
|
|
|
|
|
// Marshal and add to ExtraExtensions
|
|
|
|
ext := pkix.Extension{
|
|
|
|
// This is the defined OID for subjectAltName
|
|
|
|
Id: asn1.ObjectIdentifier{2, 5, 29, 17},
|
|
|
|
}
|
|
|
|
var err error
|
|
|
|
ext.Value, err = asn1.Marshal(rawValues)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
in.ExtraExtensions = append(in.ExtraExtensions, ext)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: Taken from the Go source code since it's not public, and used in the
|
|
|
|
// modified function below (which also uses these consts upstream)
|
|
|
|
const (
|
|
|
|
nameTypeEmail = 1
|
|
|
|
nameTypeDNS = 2
|
|
|
|
nameTypeURI = 6
|
|
|
|
nameTypeIP = 7
|
|
|
|
)
|
|
|
|
|
|
|
|
// Note: Taken from the Go source code since it's not public, plus changed to not marshal
|
|
|
|
// marshalSANs marshals a list of addresses into a the contents of an X.509
|
|
|
|
// SubjectAlternativeName extension.
|
|
|
|
func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL) []asn1.RawValue {
|
|
|
|
var rawValues []asn1.RawValue
|
|
|
|
for _, name := range dnsNames {
|
|
|
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeDNS, Class: 2, Bytes: []byte(name)})
|
|
|
|
}
|
|
|
|
for _, email := range emailAddresses {
|
|
|
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeEmail, Class: 2, Bytes: []byte(email)})
|
|
|
|
}
|
|
|
|
for _, rawIP := range ipAddresses {
|
|
|
|
// If possible, we always want to encode IPv4 addresses in 4 bytes.
|
|
|
|
ip := rawIP.To4()
|
|
|
|
if ip == nil {
|
|
|
|
ip = rawIP
|
|
|
|
}
|
|
|
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeIP, Class: 2, Bytes: ip})
|
|
|
|
}
|
|
|
|
for _, uri := range uris {
|
|
|
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeURI, Class: 2, Bytes: []byte(uri.String())})
|
|
|
|
}
|
|
|
|
return rawValues
|
|
|
|
}
|
|
|
|
|
|
|
|
func StringToOid(in string) (asn1.ObjectIdentifier, error) {
|
|
|
|
split := strings.Split(in, ".")
|
|
|
|
ret := make(asn1.ObjectIdentifier, 0, len(split))
|
|
|
|
for _, v := range split {
|
|
|
|
i, err := strconv.Atoi(v)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ret = append(ret, i)
|
|
|
|
}
|
|
|
|
return asn1.ObjectIdentifier(ret), nil
|
|
|
|
}
|
|
|
|
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
// Returns default key bits for the specified key type, or the present value
|
|
|
|
// if keyBits is non-zero.
|
|
|
|
func DefaultOrValueKeyBits(keyType string, keyBits int) (int, error) {
|
|
|
|
if keyBits == 0 {
|
|
|
|
newValue, present := defaultAlgorithmKeyBits[keyType]
|
|
|
|
if present {
|
|
|
|
keyBits = newValue
|
|
|
|
} /* else {
|
|
|
|
// We cannot return an error here as ed25519 (and potentially ed448
|
|
|
|
// in the future) aren't in defaultAlgorithmKeyBits -- the value of
|
|
|
|
// the keyBits parameter is ignored under that algorithm.
|
|
|
|
} */
|
|
|
|
}
|
|
|
|
|
|
|
|
return keyBits, nil
|
|
|
|
}
|
2021-11-12 17:18:38 +00:00
|
|
|
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
// Returns default signature hash bit length for the specified key type and
|
|
|
|
// bits, or the present value if hashBits is non-zero. Returns an error under
|
|
|
|
// certain internal circumstances.
|
|
|
|
func DefaultOrValueHashBits(keyType string, keyBits int, hashBits int) (int, error) {
|
2021-11-12 17:18:38 +00:00
|
|
|
if keyType == "ec" {
|
2022-04-07 15:52:59 +00:00
|
|
|
// Enforcement of curve moved to selectSignatureAlgorithmForECDSA. See
|
|
|
|
// note there about why.
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
} else if keyType == "rsa" && hashBits == 0 {
|
|
|
|
// To match previous behavior (and ignoring NIST's recommendations for
|
|
|
|
// hash size to align with RSA key sizes), default to SHA-2-256.
|
|
|
|
hashBits = 256
|
Fix handling of default zero SignatureBits value with Any key type in PKI Secrets Engine (#14875)
* Correctly handle minimums, default SignatureBits
When using KeyType = "any" on a role (whether explicitly or implicitly
via a sign-verbatim like operation), we need to update the value of
SignatureBits from its new value 0 to a per-key-type default value. This
will allow sign operations on these paths to function correctly, having
the correctly inferred default signature bit length.
Additionally, this allows the computed default value for key type to be
used for minimum size validation in the RSA/ECDSA paths. We additionally
enforce the 2048-minimum in this case as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix defaults and validation of "any" KeyType
When certutil is given the placeholder any keytype, it attempts to
validate and update the default zero value. However, in lacking a
default value for SignatureBits, it cannot update the value from the
zero value, thus causing validation to fail.
Add more awareness to the placeholder "any" value to certutil.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add role-based regression tests for key bits
This adds regression tests for Key Type, Key Bits, and Signature Bits
parameters on the role. We test several values, including the "any"
value to ensure it correctly restricts key sizes.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add sign-verbatim test for key type
This ensures that we test sign-verbatim against a variety of key types.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
2022-04-04 19:26:54 +00:00
|
|
|
} else if keyType == "ed25519" || keyType == "ed448" || keyType == "any" {
|
2021-11-12 17:18:38 +00:00
|
|
|
// No-op; ed25519 and ed448 internally specify their own hash and
|
|
|
|
// we do not need to select one. Double hashing isn't supported in
|
Fix handling of default zero SignatureBits value with Any key type in PKI Secrets Engine (#14875)
* Correctly handle minimums, default SignatureBits
When using KeyType = "any" on a role (whether explicitly or implicitly
via a sign-verbatim like operation), we need to update the value of
SignatureBits from its new value 0 to a per-key-type default value. This
will allow sign operations on these paths to function correctly, having
the correctly inferred default signature bit length.
Additionally, this allows the computed default value for key type to be
used for minimum size validation in the RSA/ECDSA paths. We additionally
enforce the 2048-minimum in this case as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix defaults and validation of "any" KeyType
When certutil is given the placeholder any keytype, it attempts to
validate and update the default zero value. However, in lacking a
default value for SignatureBits, it cannot update the value from the
zero value, thus causing validation to fail.
Add more awareness to the placeholder "any" value to certutil.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add role-based regression tests for key bits
This adds regression tests for Key Type, Key Bits, and Signature Bits
parameters on the role. We test several values, including the "any"
value to ensure it correctly restricts key sizes.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add sign-verbatim test for key type
This ensures that we test sign-verbatim against a variety of key types.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
2022-04-04 19:26:54 +00:00
|
|
|
// certificate signing. Additionally, the any key type can't know
|
|
|
|
// what hash algorithm to use yet, so default to zero.
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return hashBits, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validates that the combination of keyType, keyBits, and hashBits are
|
|
|
|
// valid together; replaces individual calls to ValidateSignatureLength and
|
|
|
|
// ValidateKeyTypeLength. Also updates the value of keyBits and hashBits on
|
|
|
|
// return.
|
|
|
|
func ValidateDefaultOrValueKeyTypeSignatureLength(keyType string, keyBits int, hashBits int) (int, int, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if keyBits, err = DefaultOrValueKeyBits(keyType, keyBits); err != nil {
|
|
|
|
return keyBits, hashBits, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = ValidateKeyTypeLength(keyType, keyBits); err != nil {
|
|
|
|
return keyBits, hashBits, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if hashBits, err = DefaultOrValueHashBits(keyType, keyBits, hashBits); err != nil {
|
|
|
|
return keyBits, hashBits, err
|
2021-11-23 20:28:18 +00:00
|
|
|
}
|
2021-11-12 17:18:38 +00:00
|
|
|
|
|
|
|
// Note that this check must come after we've selected a value for
|
|
|
|
// hashBits above, in the event it was left as the default, but we
|
|
|
|
// were allowed to update it.
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
if err = ValidateSignatureLength(keyType, hashBits); err != nil {
|
|
|
|
return keyBits, hashBits, err
|
2021-11-12 17:18:38 +00:00
|
|
|
}
|
|
|
|
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
return keyBits, hashBits, nil
|
2021-11-12 17:18:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validates that the length of the hash (in bits) used in the signature
|
|
|
|
// calculation is a known, approved value.
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
func ValidateSignatureLength(keyType string, hashBits int) error {
|
2022-04-07 15:52:59 +00:00
|
|
|
if keyType == "any" || keyType == "ec" || keyType == "ed25519" || keyType == "ed448" {
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
// ed25519 and ed448 include built-in hashing and is not externally
|
|
|
|
// configurable. There are three modes for each of these schemes:
|
|
|
|
//
|
|
|
|
// 1. Built-in hash (default, used in TLS, x509).
|
|
|
|
// 2. Double hash (notably used in some block-chain implementations,
|
|
|
|
// but largely regarded as a specialized use case with security
|
|
|
|
// concerns).
|
|
|
|
// 3. No hash (bring your own hash function, less commonly used).
|
|
|
|
//
|
|
|
|
// In all cases, we won't have a hash algorithm to validate here, so
|
|
|
|
// return nil.
|
Fix handling of default zero SignatureBits value with Any key type in PKI Secrets Engine (#14875)
* Correctly handle minimums, default SignatureBits
When using KeyType = "any" on a role (whether explicitly or implicitly
via a sign-verbatim like operation), we need to update the value of
SignatureBits from its new value 0 to a per-key-type default value. This
will allow sign operations on these paths to function correctly, having
the correctly inferred default signature bit length.
Additionally, this allows the computed default value for key type to be
used for minimum size validation in the RSA/ECDSA paths. We additionally
enforce the 2048-minimum in this case as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix defaults and validation of "any" KeyType
When certutil is given the placeholder any keytype, it attempts to
validate and update the default zero value. However, in lacking a
default value for SignatureBits, it cannot update the value from the
zero value, thus causing validation to fail.
Add more awareness to the placeholder "any" value to certutil.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add role-based regression tests for key bits
This adds regression tests for Key Type, Key Bits, and Signature Bits
parameters on the role. We test several values, including the "any"
value to ensure it correctly restricts key sizes.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add sign-verbatim test for key type
This ensures that we test sign-verbatim against a variety of key types.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
2022-04-04 19:26:54 +00:00
|
|
|
//
|
|
|
|
// Additionally, when KeyType is any, we can't yet validate the
|
|
|
|
// signature algorithm size, so it takes the default zero value.
|
2022-04-07 15:52:59 +00:00
|
|
|
//
|
|
|
|
// When KeyType is ec, we also can't validate this value as we're
|
|
|
|
// forcefully ignoring the users' choice and specifying a value based
|
|
|
|
// on issuer type.
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-11-12 17:18:38 +00:00
|
|
|
switch hashBits {
|
2021-09-10 21:39:05 +00:00
|
|
|
case 256:
|
|
|
|
case 384:
|
|
|
|
case 512:
|
|
|
|
default:
|
2021-11-12 17:18:38 +00:00
|
|
|
return fmt.Errorf("unsupported hash signature algorithm: %d", hashBits)
|
2021-09-10 21:39:05 +00:00
|
|
|
}
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
|
2021-09-10 21:39:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-09 15:43:11 +00:00
|
|
|
func ValidateKeyTypeLength(keyType string, keyBits int) error {
|
|
|
|
switch keyType {
|
|
|
|
case "rsa":
|
Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits
This allows the key_bits field to take a universal default value, 0,
which, depending on key_type, gets adjusted appropriately into a
specific default value (rsa->2048, ec->256, ignored under ed25519).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Handle universal default key size in certutil
Also move RSA < 2048 error message into certutil directly, instead of in
ca_util/path_roles.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing RSA key sizes to pki/backend_test.go
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Switch to returning updated values
When determining the default, don't pass in pointer types, but instead
return the newly updated value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Re-add fix for ed25519 from #13254
Ed25519 internally specifies a hash length; by changing the default from
256 to 0, we fail validation in ValidateSignatureLength(...) unless we
specify the key algorithm.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2021-12-13 20:26:42 +00:00
|
|
|
if keyBits < rsaMinimumSecureKeySize {
|
|
|
|
return fmt.Errorf("RSA keys < %d bits are unsafe and not supported: got %d", rsaMinimumSecureKeySize, keyBits)
|
|
|
|
}
|
|
|
|
|
2019-05-09 15:43:11 +00:00
|
|
|
switch keyBits {
|
|
|
|
case 2048:
|
2020-06-05 19:54:41 +00:00
|
|
|
case 3072:
|
2019-05-09 15:43:11 +00:00
|
|
|
case 4096:
|
|
|
|
case 8192:
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unsupported bit length for RSA key: %d", keyBits)
|
|
|
|
}
|
|
|
|
case "ec":
|
2021-11-12 17:18:38 +00:00
|
|
|
_, present := expectedNISTPCurveHashBits[keyBits]
|
|
|
|
if !present {
|
2019-05-09 15:43:11 +00:00
|
|
|
return fmt.Errorf("unsupported bit length for EC key: %d", keyBits)
|
|
|
|
}
|
2021-10-05 15:28:49 +00:00
|
|
|
case "any", "ed25519":
|
2019-05-09 15:43:11 +00:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("unknown key type %s", keyType)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
// CreateCertificate uses CreationBundle and the default rand.Reader to
|
|
|
|
// generate a cert/keypair.
|
2019-05-09 15:43:11 +00:00
|
|
|
func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) {
|
2022-01-27 04:06:25 +00:00
|
|
|
return createCertificate(data, rand.Reader, generatePrivateKey)
|
2021-01-08 17:48:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateCertificateWithRandomSource uses CreationBundle and a custom
|
|
|
|
// io.Reader for randomness to generate a cert/keypair.
|
|
|
|
func CreateCertificateWithRandomSource(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) {
|
2022-01-27 04:06:25 +00:00
|
|
|
return createCertificate(data, randReader, generatePrivateKey)
|
2021-01-08 17:48:27 +00:00
|
|
|
}
|
|
|
|
|
2022-01-27 04:06:25 +00:00
|
|
|
// KeyGenerator Allow us to override how/what generates the private key
|
|
|
|
type KeyGenerator func(keyType string, keyBits int, container ParsedPrivateKeyContainer, entropyReader io.Reader) error
|
|
|
|
|
|
|
|
func CreateCertificateWithKeyGenerator(data *CreationBundle, randReader io.Reader, keyGenerator KeyGenerator) (*ParsedCertBundle, error) {
|
|
|
|
return createCertificate(data, randReader, keyGenerator)
|
|
|
|
}
|
|
|
|
|
|
|
|
func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGenerator KeyGenerator) (*ParsedCertBundle, error) {
|
2019-05-09 15:43:11 +00:00
|
|
|
var err error
|
|
|
|
result := &ParsedCertBundle{}
|
|
|
|
|
|
|
|
serialNumber, err := GenerateSerialNumber()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-01-27 04:06:25 +00:00
|
|
|
if err := privateKeyGenerator(data.Params.KeyType,
|
2019-05-09 15:43:11 +00:00
|
|
|
data.Params.KeyBits,
|
2021-01-08 17:48:27 +00:00
|
|
|
result, randReader); err != nil {
|
2019-05-09 15:43:11 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
subjKeyID, err := GetSubjKeyID(result.PrivateKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("error getting subject key ID: %s", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
certTemplate := &x509.Certificate{
|
|
|
|
SerialNumber: serialNumber,
|
|
|
|
NotBefore: time.Now().Add(-30 * time.Second),
|
|
|
|
NotAfter: data.Params.NotAfter,
|
|
|
|
IsCA: false,
|
|
|
|
SubjectKeyId: subjKeyID,
|
|
|
|
Subject: data.Params.Subject,
|
|
|
|
DNSNames: data.Params.DNSNames,
|
|
|
|
EmailAddresses: data.Params.EmailAddresses,
|
|
|
|
IPAddresses: data.Params.IPAddresses,
|
|
|
|
URIs: data.Params.URIs,
|
|
|
|
}
|
|
|
|
if data.Params.NotBeforeDuration > 0 {
|
|
|
|
certTemplate.NotBefore = time.Now().Add(-1 * data.Params.NotBeforeDuration)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := HandleOtherSANs(certTemplate, data.Params.OtherSANs); err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add this before calling addKeyUsages
|
|
|
|
if data.SigningBundle == nil {
|
|
|
|
certTemplate.IsCA = true
|
|
|
|
} else if data.Params.BasicConstraintsValidForNonCA {
|
|
|
|
certTemplate.BasicConstraintsValid = true
|
|
|
|
certTemplate.IsCA = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// This will only be filled in from the generation paths
|
|
|
|
if len(data.Params.PermittedDNSDomains) > 0 {
|
|
|
|
certTemplate.PermittedDNSDomains = data.Params.PermittedDNSDomains
|
|
|
|
certTemplate.PermittedDNSDomainsCritical = true
|
|
|
|
}
|
|
|
|
|
|
|
|
AddPolicyIdentifiers(data, certTemplate)
|
|
|
|
|
|
|
|
AddKeyUsages(data, certTemplate)
|
|
|
|
|
|
|
|
AddExtKeyUsageOids(data, certTemplate)
|
|
|
|
|
|
|
|
certTemplate.IssuingCertificateURL = data.Params.URLs.IssuingCertificates
|
|
|
|
certTemplate.CRLDistributionPoints = data.Params.URLs.CRLDistributionPoints
|
|
|
|
certTemplate.OCSPServer = data.Params.URLs.OCSPServers
|
|
|
|
|
|
|
|
var certBytes []byte
|
|
|
|
if data.SigningBundle != nil {
|
|
|
|
switch data.SigningBundle.PrivateKeyType {
|
|
|
|
case RSAPrivateKey:
|
2021-09-10 21:39:05 +00:00
|
|
|
switch data.Params.SignatureBits {
|
|
|
|
case 256:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
|
|
|
|
case 384:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
|
|
|
|
case 512:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
|
|
|
|
}
|
2021-10-05 15:28:49 +00:00
|
|
|
case Ed25519PrivateKey:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.PureEd25519
|
2019-05-09 15:43:11 +00:00
|
|
|
case ECPrivateKey:
|
2021-11-04 20:33:01 +00:00
|
|
|
certTemplate.SignatureAlgorithm = selectSignatureAlgorithmForECDSA(data.SigningBundle.PrivateKey.Public(), data.Params.SignatureBits)
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
caCert := data.SigningBundle.Certificate
|
|
|
|
certTemplate.AuthorityKeyId = caCert.SubjectKeyId
|
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, result.PrivateKey.Public(), data.SigningBundle.PrivateKey)
|
2019-05-09 15:43:11 +00:00
|
|
|
} else {
|
|
|
|
// Creating a self-signed root
|
|
|
|
if data.Params.MaxPathLength == 0 {
|
|
|
|
certTemplate.MaxPathLen = 0
|
|
|
|
certTemplate.MaxPathLenZero = true
|
|
|
|
} else {
|
|
|
|
certTemplate.MaxPathLen = data.Params.MaxPathLength
|
|
|
|
}
|
|
|
|
|
|
|
|
switch data.Params.KeyType {
|
|
|
|
case "rsa":
|
2021-09-10 21:39:05 +00:00
|
|
|
switch data.Params.SignatureBits {
|
|
|
|
case 256:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
|
|
|
|
case 384:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
|
|
|
|
case 512:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
|
|
|
|
}
|
2021-10-05 15:28:49 +00:00
|
|
|
case "ed25519":
|
|
|
|
certTemplate.SignatureAlgorithm = x509.PureEd25519
|
2019-05-09 15:43:11 +00:00
|
|
|
case "ec":
|
2021-11-04 20:33:01 +00:00
|
|
|
certTemplate.SignatureAlgorithm = selectSignatureAlgorithmForECDSA(result.PrivateKey.Public(), data.Params.SignatureBits)
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
certTemplate.AuthorityKeyId = subjKeyID
|
|
|
|
certTemplate.BasicConstraintsValid = true
|
2021-01-08 17:48:27 +00:00
|
|
|
certBytes, err = x509.CreateCertificate(randReader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey)
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.CertificateBytes = certBytes
|
|
|
|
result.Certificate, err = x509.ParseCertificate(certBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
if data.SigningBundle != nil {
|
2022-05-11 17:09:18 +00:00
|
|
|
if (len(data.SigningBundle.Certificate.AuthorityKeyId) > 0 &&
|
|
|
|
!bytes.Equal(data.SigningBundle.Certificate.AuthorityKeyId, data.SigningBundle.Certificate.SubjectKeyId)) ||
|
|
|
|
data.Params.ForceAppendCaChain {
|
|
|
|
var chain []*CertBlock
|
|
|
|
|
|
|
|
signingChain := data.SigningBundle.CAChain
|
|
|
|
// Some bundles already include the root included in the chain, so don't include it twice.
|
|
|
|
if len(signingChain) == 0 || !bytes.Equal(signingChain[0].Bytes, data.SigningBundle.CertificateBytes) {
|
|
|
|
chain = append(chain, &CertBlock{
|
2019-05-09 15:43:11 +00:00
|
|
|
Certificate: data.SigningBundle.Certificate,
|
|
|
|
Bytes: data.SigningBundle.CertificateBytes,
|
2022-05-11 17:09:18 +00:00
|
|
|
})
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
2022-05-11 17:09:18 +00:00
|
|
|
|
|
|
|
if len(signingChain) > 0 {
|
|
|
|
chain = append(chain, signingChain...)
|
|
|
|
}
|
|
|
|
|
|
|
|
result.CAChain = chain
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2021-11-04 20:33:01 +00:00
|
|
|
func selectSignatureAlgorithmForECDSA(pub crypto.PublicKey, signatureBits int) x509.SignatureAlgorithm {
|
2022-04-07 15:52:59 +00:00
|
|
|
// Previously we preferred the user-specified signature bits for ECDSA
|
|
|
|
// keys. However, this could result in using a longer hash function than
|
|
|
|
// the underlying NIST P-curve will encode (e.g., a SHA-512 hash with a
|
|
|
|
// P-256 key). This isn't ideal: the hash is implicitly truncated
|
|
|
|
// (effectively turning it into SHA-512/256) and we then need to rely
|
|
|
|
// on the prefix security of the hash. Since both NIST and Mozilla guidance
|
|
|
|
// suggest instead using the correct hash function, we should prefer that
|
|
|
|
// over the operator-specified signatureBits.
|
|
|
|
//
|
|
|
|
// Lastly, note that pub above needs to be the _signer's_ public key;
|
|
|
|
// the issue with DefaultOrValueHashBits is that it is called at role
|
|
|
|
// configuration time, which might _precede_ issuer generation. Thus
|
|
|
|
// it only has access to the desired key type and not the actual issuer.
|
|
|
|
// The reference from that function is reproduced below:
|
|
|
|
//
|
|
|
|
// > To comply with BSI recommendations Section 4.2 and Mozilla root
|
|
|
|
// > store policy section 5.1.2, enforce that NIST P-curves use a hash
|
|
|
|
// > length corresponding to curve length. Note that ed25519 does not
|
|
|
|
// > implement the "ec" key type.
|
2021-11-04 20:33:01 +00:00
|
|
|
key, ok := pub.(*ecdsa.PublicKey)
|
|
|
|
if !ok {
|
|
|
|
return x509.ECDSAWithSHA256
|
|
|
|
}
|
|
|
|
switch key.Curve {
|
|
|
|
case elliptic.P224(), elliptic.P256():
|
|
|
|
return x509.ECDSAWithSHA256
|
|
|
|
case elliptic.P384():
|
|
|
|
return x509.ECDSAWithSHA384
|
|
|
|
case elliptic.P521():
|
|
|
|
return x509.ECDSAWithSHA512
|
|
|
|
default:
|
|
|
|
return x509.ECDSAWithSHA256
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-09 15:43:11 +00:00
|
|
|
var oidExtensionBasicConstraints = []int{2, 5, 29, 19}
|
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
// CreateCSR creates a CSR with the default rand.Reader to
|
|
|
|
// generate a cert/keypair. This is currently only meant
|
|
|
|
// for use when generating an intermediate certificate.
|
2019-05-09 15:43:11 +00:00
|
|
|
func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle, error) {
|
2022-01-27 04:06:25 +00:00
|
|
|
return createCSR(data, addBasicConstraints, rand.Reader, generatePrivateKey)
|
2021-01-08 17:48:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateCSRWithRandomSource creates a CSR with a custom io.Reader
|
|
|
|
// for randomness to generate a cert/keypair.
|
|
|
|
func CreateCSRWithRandomSource(data *CreationBundle, addBasicConstraints bool, randReader io.Reader) (*ParsedCSRBundle, error) {
|
2022-01-27 04:06:25 +00:00
|
|
|
return createCSR(data, addBasicConstraints, randReader, generatePrivateKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateCSRWithKeyGenerator creates a CSR with a custom io.Reader
|
|
|
|
// for randomness to generate a cert/keypair with the provided private key generator.
|
|
|
|
func CreateCSRWithKeyGenerator(data *CreationBundle, addBasicConstraints bool, randReader io.Reader, keyGenerator KeyGenerator) (*ParsedCSRBundle, error) {
|
|
|
|
return createCSR(data, addBasicConstraints, randReader, keyGenerator)
|
2021-01-08 17:48:27 +00:00
|
|
|
}
|
|
|
|
|
2022-01-27 04:06:25 +00:00
|
|
|
func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Reader, keyGenerator KeyGenerator) (*ParsedCSRBundle, error) {
|
2019-05-09 15:43:11 +00:00
|
|
|
var err error
|
|
|
|
result := &ParsedCSRBundle{}
|
|
|
|
|
2022-01-27 04:06:25 +00:00
|
|
|
if err := keyGenerator(data.Params.KeyType,
|
2019-05-09 15:43:11 +00:00
|
|
|
data.Params.KeyBits,
|
2021-01-08 17:48:27 +00:00
|
|
|
result, randReader); err != nil {
|
2019-05-09 15:43:11 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Like many root CAs, other information is ignored
|
|
|
|
csrTemplate := &x509.CertificateRequest{
|
|
|
|
Subject: data.Params.Subject,
|
|
|
|
DNSNames: data.Params.DNSNames,
|
|
|
|
EmailAddresses: data.Params.EmailAddresses,
|
|
|
|
IPAddresses: data.Params.IPAddresses,
|
|
|
|
URIs: data.Params.URIs,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := HandleOtherCSRSANs(csrTemplate, data.Params.OtherSANs); err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
|
|
|
|
}
|
|
|
|
|
|
|
|
if addBasicConstraints {
|
|
|
|
type basicConstraints struct {
|
|
|
|
IsCA bool `asn1:"optional"`
|
|
|
|
MaxPathLen int `asn1:"optional,default:-1"`
|
|
|
|
}
|
|
|
|
val, err := asn1.Marshal(basicConstraints{IsCA: true, MaxPathLen: -1})
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling basic constraints: {{err}}", err).Error()}
|
|
|
|
}
|
|
|
|
ext := pkix.Extension{
|
|
|
|
Id: oidExtensionBasicConstraints,
|
|
|
|
Value: val,
|
|
|
|
Critical: true,
|
|
|
|
}
|
|
|
|
csrTemplate.ExtraExtensions = append(csrTemplate.ExtraExtensions, ext)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch data.Params.KeyType {
|
|
|
|
case "rsa":
|
|
|
|
csrTemplate.SignatureAlgorithm = x509.SHA256WithRSA
|
|
|
|
case "ec":
|
|
|
|
csrTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
|
2021-10-05 15:28:49 +00:00
|
|
|
case "ed25519":
|
|
|
|
csrTemplate.SignatureAlgorithm = x509.PureEd25519
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
csr, err := x509.CreateCertificateRequest(randReader, csrTemplate, result.PrivateKey)
|
2019-05-09 15:43:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.CSRBytes = csr
|
|
|
|
result.CSR, err = x509.ParseCertificateRequest(csr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %v", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
// SignCertificate performs the heavy lifting
|
|
|
|
// of generating a certificate from a CSR.
|
2019-05-09 15:43:11 +00:00
|
|
|
// Returns a ParsedCertBundle sans private keys.
|
|
|
|
func SignCertificate(data *CreationBundle) (*ParsedCertBundle, error) {
|
2021-01-08 17:48:27 +00:00
|
|
|
return signCertificate(data, rand.Reader)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SignCertificateWithRandomSource generates a certificate
|
|
|
|
// from a CSR, using custom randomness from the randReader.
|
|
|
|
// Returns a ParsedCertBundle sans private keys.
|
|
|
|
func SignCertificateWithRandomSource(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) {
|
|
|
|
return signCertificate(data, randReader)
|
|
|
|
}
|
|
|
|
|
|
|
|
func signCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) {
|
2019-05-09 15:43:11 +00:00
|
|
|
switch {
|
|
|
|
case data == nil:
|
|
|
|
return nil, errutil.UserError{Err: "nil data bundle given to signCertificate"}
|
|
|
|
case data.Params == nil:
|
|
|
|
return nil, errutil.UserError{Err: "nil parameters given to signCertificate"}
|
|
|
|
case data.SigningBundle == nil:
|
|
|
|
return nil, errutil.UserError{Err: "nil signing bundle given to signCertificate"}
|
|
|
|
case data.CSR == nil:
|
|
|
|
return nil, errutil.UserError{Err: "nil csr given to signCertificate"}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := data.CSR.CheckSignature()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.UserError{Err: "request signature invalid"}
|
|
|
|
}
|
|
|
|
|
|
|
|
result := &ParsedCertBundle{}
|
|
|
|
|
|
|
|
serialNumber, err := GenerateSerialNumber()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-01-28 15:46:51 +00:00
|
|
|
subjKeyID, err := getSubjectKeyID(data.CSR.PublicKey)
|
2019-05-09 15:43:11 +00:00
|
|
|
if err != nil {
|
2022-01-28 15:46:51 +00:00
|
|
|
return nil, err
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
caCert := data.SigningBundle.Certificate
|
|
|
|
|
|
|
|
certTemplate := &x509.Certificate{
|
|
|
|
SerialNumber: serialNumber,
|
|
|
|
Subject: data.Params.Subject,
|
|
|
|
NotBefore: time.Now().Add(-30 * time.Second),
|
|
|
|
NotAfter: data.Params.NotAfter,
|
|
|
|
SubjectKeyId: subjKeyID[:],
|
|
|
|
AuthorityKeyId: caCert.SubjectKeyId,
|
|
|
|
}
|
|
|
|
if data.Params.NotBeforeDuration > 0 {
|
|
|
|
certTemplate.NotBefore = time.Now().Add(-1 * data.Params.NotBeforeDuration)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch data.SigningBundle.PrivateKeyType {
|
|
|
|
case RSAPrivateKey:
|
2021-09-10 21:39:05 +00:00
|
|
|
switch data.Params.SignatureBits {
|
|
|
|
case 256:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
|
|
|
|
case 384:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
|
|
|
|
case 512:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
|
|
|
|
}
|
2019-05-09 15:43:11 +00:00
|
|
|
case ECPrivateKey:
|
2021-09-10 21:39:05 +00:00
|
|
|
switch data.Params.SignatureBits {
|
|
|
|
case 256:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
|
|
|
|
case 384:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA384
|
|
|
|
case 512:
|
|
|
|
certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA512
|
|
|
|
}
|
2019-05-09 15:43:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if data.Params.UseCSRValues {
|
|
|
|
certTemplate.Subject = data.CSR.Subject
|
|
|
|
certTemplate.Subject.ExtraNames = certTemplate.Subject.Names
|
|
|
|
|
|
|
|
certTemplate.DNSNames = data.CSR.DNSNames
|
|
|
|
certTemplate.EmailAddresses = data.CSR.EmailAddresses
|
|
|
|
certTemplate.IPAddresses = data.CSR.IPAddresses
|
|
|
|
certTemplate.URIs = data.CSR.URIs
|
|
|
|
|
|
|
|
for _, name := range data.CSR.Extensions {
|
|
|
|
if !name.Id.Equal(oidExtensionBasicConstraints) {
|
|
|
|
certTemplate.ExtraExtensions = append(certTemplate.ExtraExtensions, name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
certTemplate.DNSNames = data.Params.DNSNames
|
|
|
|
certTemplate.EmailAddresses = data.Params.EmailAddresses
|
|
|
|
certTemplate.IPAddresses = data.Params.IPAddresses
|
|
|
|
certTemplate.URIs = data.Params.URIs
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := HandleOtherSANs(certTemplate, data.Params.OtherSANs); err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
|
|
|
|
}
|
|
|
|
|
|
|
|
AddPolicyIdentifiers(data, certTemplate)
|
|
|
|
|
|
|
|
AddKeyUsages(data, certTemplate)
|
|
|
|
|
|
|
|
AddExtKeyUsageOids(data, certTemplate)
|
|
|
|
|
|
|
|
var certBytes []byte
|
|
|
|
|
|
|
|
certTemplate.IssuingCertificateURL = data.Params.URLs.IssuingCertificates
|
|
|
|
certTemplate.CRLDistributionPoints = data.Params.URLs.CRLDistributionPoints
|
|
|
|
certTemplate.OCSPServer = data.SigningBundle.URLs.OCSPServers
|
|
|
|
|
|
|
|
if data.Params.IsCA {
|
|
|
|
certTemplate.BasicConstraintsValid = true
|
|
|
|
certTemplate.IsCA = true
|
|
|
|
|
|
|
|
if data.SigningBundle.Certificate.MaxPathLen == 0 &&
|
|
|
|
data.SigningBundle.Certificate.MaxPathLenZero {
|
|
|
|
return nil, errutil.UserError{Err: "signing certificate has a max path length of zero, and cannot issue further CA certificates"}
|
|
|
|
}
|
|
|
|
|
|
|
|
certTemplate.MaxPathLen = data.Params.MaxPathLength
|
|
|
|
if certTemplate.MaxPathLen == 0 {
|
|
|
|
certTemplate.MaxPathLenZero = true
|
|
|
|
}
|
|
|
|
} else if data.Params.BasicConstraintsValidForNonCA {
|
|
|
|
certTemplate.BasicConstraintsValid = true
|
|
|
|
certTemplate.IsCA = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(data.Params.PermittedDNSDomains) > 0 {
|
|
|
|
certTemplate.PermittedDNSDomains = data.Params.PermittedDNSDomains
|
|
|
|
certTemplate.PermittedDNSDomainsCritical = true
|
|
|
|
}
|
|
|
|
|
2021-01-08 17:48:27 +00:00
|
|
|
certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, data.CSR.PublicKey, data.SigningBundle.PrivateKey)
|
2019-05-09 15:43:11 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.CertificateBytes = certBytes
|
|
|
|
result.Certificate, err = x509.ParseCertificate(certBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
|
|
|
|
}
|
|
|
|
|
2022-05-11 17:09:18 +00:00
|
|
|
result.CAChain = data.SigningBundle.GetFullChain()
|
2019-05-09 15:43:11 +00:00
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
2020-02-13 17:56:29 +00:00
|
|
|
|
|
|
|
func NewCertPool(reader io.Reader) (*x509.CertPool, error) {
|
|
|
|
pemBlock, err := ioutil.ReadAll(reader)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
certs, err := parseCertsPEM(pemBlock)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error reading certs: %s", err)
|
|
|
|
}
|
|
|
|
pool := x509.NewCertPool()
|
|
|
|
for _, cert := range certs {
|
|
|
|
pool.AddCert(cert)
|
|
|
|
}
|
|
|
|
return pool, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseCertsPEM returns the x509.Certificates contained in the given PEM-encoded byte array
|
|
|
|
// Returns an error if a certificate could not be parsed, or if the data does not contain any certificates
|
|
|
|
func parseCertsPEM(pemCerts []byte) ([]*x509.Certificate, error) {
|
|
|
|
ok := false
|
|
|
|
certs := []*x509.Certificate{}
|
|
|
|
for len(pemCerts) > 0 {
|
|
|
|
var block *pem.Block
|
|
|
|
block, pemCerts = pem.Decode(pemCerts)
|
|
|
|
if block == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Only use PEM "CERTIFICATE" blocks without extra headers
|
|
|
|
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return certs, err
|
|
|
|
}
|
|
|
|
|
|
|
|
certs = append(certs, cert)
|
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
return certs, errors.New("data does not contain any valid RSA or ECDSA certificates")
|
|
|
|
}
|
|
|
|
return certs, nil
|
|
|
|
}
|
2021-10-07 19:17:31 +00:00
|
|
|
|
|
|
|
// GetPublicKeySize returns the key size in bits for a given arbitrary crypto.PublicKey
|
|
|
|
// Returns -1 for an unsupported key type.
|
|
|
|
func GetPublicKeySize(key crypto.PublicKey) int {
|
|
|
|
if key, ok := key.(*rsa.PublicKey); ok {
|
|
|
|
return key.Size() * 8
|
|
|
|
}
|
|
|
|
if key, ok := key.(*ecdsa.PublicKey); ok {
|
|
|
|
return key.Params().BitSize
|
|
|
|
}
|
|
|
|
if key, ok := key.(ed25519.PublicKey); ok {
|
|
|
|
return len(key) * 8
|
|
|
|
}
|
|
|
|
if key, ok := key.(dsa.PublicKey); ok {
|
|
|
|
return key.Y.BitLen()
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1
|
|
|
|
}
|
Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod (#15277)
* Starter PKI CA Storage API (#14796)
* Simple starting PKI storage api for CA rotation
* Add key and issuer storage apis
* Add listKeys and listIssuers storage implementations
* Add simple keys and issuers configuration storage api methods
* Handle resolving key, issuer references
The API context will usually have a user-specified reference to the key.
This is either the literal string "default" to select the default key,
an identifier of the key, or a slug name for the key. Here, we wish to
resolve this reference to an actual identifier that can be understood by
storage.
Also adds the missing Name field to keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add method to fetch an issuer's cert bundle
This adds a method to construct a certutil.CertBundle from the specified
issuer identifier, optionally loading its corresponding key for signing.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor certutil PrivateKey PEM handling
This refactors the parsing of PrivateKeys from PEM blobs into shared
methods (ParsePEMKey, ParseDERKey) that can be reused by the existing
Bundle parsing logic (ParsePEMBundle) or independently in the new
issuers/key-based PKI storage code.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add importKey, importCert to PKI storage
importKey is generally preferable to the low-level writeKey for adding
new entries. This takes only the contents of the private key (as a
string -- so a PEM bundle or a managed key handle) and checks if it
already exists in the storage.
If it does, it returns the existing key instance.
Otherwise, we create a new one. In the process, we detect any issuers
using this key and link them back to the new key entry.
The same holds for importCert over importKey, with the note that keys
are not modified when importing certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for importing issuers, keys
This adds tests for importing keys and issuers into the new storage
layout, ensuring that identifiers are correctly inferred and linked.
Note that directly writing entries to storage (writeKey/writeissuer)
will take KeyID links from the parent entry and should not be used for
import; only existing entries should be updated with this info.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Implement PKI storage migration.
- Hook into the backend::initialize function, calling the migration on a primary only.
- Migrate an existing certificate bundle to the new issuers and key layout
* Make fetchCAInfo aware of new storage layout
This allows fetchCAInfo to fetch a specified issuer, via a reference
parameter provided by the user. We pass that into the storage layer and
have it return a cert bundle for us. Finally, we need to validate that
it truly has the key desired.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Begin /issuers API endpoints
This implements the fetch operations around issuers in the PKI Secrets
Engine. We implement the following operations:
- LIST /issuers - returns a list of known issuers' IDs and names.
- GET /issuer/:ref - returns a JSON blob with information about this
issuer.
- POST /issuer/:ref - allows configuring information about issuers,
presently just its name.
- DELETE /issuer/:ref - allows deleting the specified issuer.
- GET /issuer/:ref/{der,pem} - returns a raw API response with just
the DER (or PEM) of the issuer's certificate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add import to PKI Issuers API
This adds the two core import code paths to the API:
/issuers/import/cert and /issuers/import/bundle. The former differs from
the latter in that the latter allows the import of keys. This allows
operators to restrict importing of keys to privileged roles, while
allowing more operators permission to import additional certificates
(not used for signing, but instead for path/chain building).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-intermediate endpoint
This endpoint allows existing issuers to be used to sign intermediate
CA certificates. In the process, we've updated the existing
/root/sign-intermediate endpoint to be equivalent to a call to
/issuer/default/sign-intermediate.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-self-issued endpoint
This endpoint allows existing issuers to be used to sign self-signed
certificates. In the process, we've updated the existing
/root/sign-self-issued endpoint to be equivalent to a call to
/issuer/default/sign-self-issued.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/sign-verbatim endpoint
This endpoint allows existing issuers to be used to directly sign CSRs.
In the process, we've updated the existing /sign-verbatim endpoint to be
equivalent to a call to /issuer/:ref/sign-verbatim.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow configuration of default issuers
Using the new updateDefaultIssuerId(...) from the storage migration PR
allows for easy implementation of configuring the default issuer. We
restrict callers from setting blank defaults and setting default to
default.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix fetching default issuers
After setting a default issuer, one should be able to use the old /ca,
/ca_chain, and /cert/{ca,ca_chain} endpoints to fetch the default issuer
(and its chain). Update the fetchCertBySerial helper to no longer
support fetching the ca and prefer fetchCAInfo for that instead (as
we've already updated that to support fetching the new issuer location).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add /issuer/:ref/{sign,issue}/:role
This updates the /sign and /issue endpoints, allowing them to take the
default issuer (if none is provided by a role) and adding
issuer-specific versions of them.
Note that at this point in time, the behavior isn't yet ideal (as
/sign/:role allows adding the ref=... parameter to override the default
issuer); a later change adding role-based issuer specification will fix
this incorrect behavior.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support root issuer generation
* Add support for issuer generate intermediate end-point
* Update issuer and key arguments to consistent values
- Update all new API endpoints to use the new agreed upon argument names.
- issuer_ref & key_ref to refer to existing
- issuer_name & key_name for new definitions
- Update returned values to always user issuer_id and key_id
* Add utility methods to fetch common ref and name arguments
- Add utility methods to fetch the issuer_name, issuer_ref, key_name and key_ref arguments from data fields.
- Centralize the logic to clean up these inputs and apply various validations to all of them.
* Rename common PKI backend handlers
- Use the buildPath convention for the function name instead of common...
* Move setting PKI defaults from writeCaBundle to proper import{keys,issuer} methods
- PR feedback, move setting up the default configuration references within
the import methods instead of within the writeCaBundle method. This should
now cover all use cases of us setting up the defaults properly.
* Introduce constants for issuer_ref, rename isKeyDefaultSet...
* Fix legacy PKI sign-verbatim api path
- Addresses some test failures due to an incorrect refactoring of a legacy api
path /sign-verbatim within PKI
* Use import code to handle intermediate, config/ca
The existing bundle import code will satisfy the intermediate import;
use it instead of the old ca_bundle import logic. Additionally, update
/config/ca to use the new import code as well.
While testing, a panic was discovered:
> reflect.Value.SetMapIndex: value of type string is not assignable to type pki.keyId
This was caused by returning a map with type issuerId->keyId; instead
switch to returning string->string maps so the audit log can properly
HMAC them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify error message on missing defaults
When the default issuer and key are missing (and haven't yet been
specified), we should clarify that error message.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test semantics for new changes
This makes two minor changes to the existing test suite:
1. Importing partial bundles should now succeed, where they'd
previously error.
2. fetchCertBySerial no longer handles CA certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add support for deleting all keys, issuers
The old DELETE /root code must now delete all keys and issuers for
backwards compatibility. We strongly suggest calling individual delete
methods (DELETE /key/:key_ref or DELETE /issuer/:issuer_ref) instead,
for finer control.
In the process, we detect whether the deleted key/issuers was set as the
default. This will allow us to warn (from the single key/deletion issuer
code) whether or not the default was deleted (while allowing the
operation to succeed).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Introduce defaultRef constant within PKI
- Replace hardcoded "default" references with a constant to easily identify various usages.
- Use the addIssuerRefField function instead of redefining the field in various locations.
* Rework PKI test TestBackend_Root_Idempotency
- Validate that generate/root calls are no longer idempotent, but the bundle importing
does not generate new keys/issuers
- As before make sure that the delete root api resets everything
- Address a bug within the storage that we bombed when we had multiple different
key types within storage.
* Assign Name=current to migrated key and issuer
- Detail I missed from the RFC was to assign the Name field as "current" for migrated key and issuer.
* Build CRL upon PKI intermediary set-signed api called
- Add a call to buildCRL if we created an issuer within pathImportIssuers
- Augment existing FullCAChain to verify we have a proper CRL post set-signed api call
- Remove a code block writing out "ca" storage entry that is no longer used.
* Identify which certificate or key failed
When importing complex chains, we should identify in which certificate
or key the failure occurred.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI migration writes out empty migration log entry
- Since the elements of the struct were not exported we serialized an empty
migration log to disk and would re-run the migration
* Add chain-building logic to PKI issuers path
With the one-entry-per-issuer approach, CA Chains become implicitly
constructed from the pool of issuers. This roughly matches the existing
expectations from /config/ca (wherein a chain could be provided) and
/intemediate/set-signed (where a chain may be provided). However, in
both of those cases, we simply accepted a chain. Here, we need to be
able to reconstruct the chain from parts on disk.
However, with potential rotation of roots, we need to be aware of
disparate chains. Simply concating together all issuers isn't
sufficient. Thus we need to be able to parse a certificate's Issuer and
Subject field and reconstruct valid (and potentially parallel)
parent<->child mappings.
This attempts to handle roots, intermediates, cross-signed
intermediates, cross-signed roots, and rotated keys (wherein one might
not have a valid signature due to changed key material with the same
subject).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Return CA Chain when fetching issuers
This returns the CA Chain attribute of an issuer, showing its computed
chain based on other issuers in the database, when fetching a specific
issuer.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add testing for chain building
Using the issuance infrastructure, we generate new certificates (either
roots or intermediates), positing that this is roughly equivalent to
importing an external bundle (minus error handling during partial
imports). This allows us to incrementally construct complex chains,
creating reissuance cliques and cross-signing cycles.
By using ECDSA certificates, we avoid high signature verification and
key generation times.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow manual construction of issuer chain
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix handling of duplicate names
With the new issuer field (manual_chain), we can no longer err when a
name already exists: we might be updating the existing issuer (with the
same name), but changing its manual_chain field. Detect this error and
correctly handle it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for manual chain building
We break the clique, instead building these chains manually, ensuring
that the remaining chains do not change and only the modified certs
change. We then reset them (back to implicit chain building) and ensure
we get the same results as earlier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter verification of issuers PEM format
This ensures each issuer is only a single certificate entry (as
validated by count and parsing) without any trailing data.
We further ensure that each certificate PEM has leading and trailing
spaces removed with only a single trailing new line remaining.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix full chain building
Don't set the legacy IssuingCA field on the certificate bundle, as we
prefer the CAChain field over it.
Additionally, building the full chain could result in duplicate
certificates when the CAChain included the leaf certificate itself. When
building the full chain, ensure we don't include the bundle's
certificate twice.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add stricter tests for full chain construction
We wish to ensure that each desired certificate in the chain is only
present once.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Rename PKI types to avoid constant variable name collisions
keyId -> keyID
issuerId -> issuerID
key -> keyEntry
issuer -> issuerEntry
keyConfig -> keyConfigEntry
issuerConfig -> issuerConfigEntry
* Update CRL handling for multiple issuers
When building CRLs, we've gotta make sure certs issued by that issuer
land up on that issuer's CRL and not some other CRL. If no CRL is
found (matching a cert), we'll place it on the default CRL.
However, in the event of equivalent issuers (those with the same subject
AND the same key material) -- perhaps due to reissuance -- we'll only
create a single (unified) CRL for them.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching updated CRL locations
This updates fetchCertBySerial to support querying the default issuer's
CRL.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL storage location test case
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update to CRLv2 Format to copy RawIssuer
When using the older Certificate.CreateCRL(...) call, Go's x509 library
copies the parsed pkix.Name version of the CRL Issuer's Subject field.
For certain constructed CAs, this fails since pkix.Name is not suitable
for round-tripping. This also builds a CRLv1 (per RFC 5280) CRL.
In updating to the newer x509.CreateRevocationList(...) call, we can
construct the CRL in the CRLv2 format and correctly copy the issuer's
name. However, this requires holding an additional field per-CRL, the
CRLNumber field, which is required in Go's implementation of CRLv2
(though OPTIONAL in the spec). We store this on the new
LocalCRLConfigEntry object, per-CRL.
Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add comment regarding CRL non-assignment in GOTO
In previous versions of Vault, it was possible to sign an empty CRL
(when the CRL was disabled and a force-rebuild was requested). Add a
comment about this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow fetching the specified issuer's CRL
We add a new API endpoint to fetch the specified issuer's CRL directly
(rather than the default issuer's CRL at /crl and /certs/crl). We also
add a new test to validate the CRL in a multi-root scenario and ensure
it is signed with the correct keys.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add new PKI key prefix to seal wrapped storage (#15126)
* Refactor common backend initialization within backend_test
- Leverage an existing helper method within the PKI backend tests to setup a PKI backend with storage.
* Add ability to read legacy cert bundle if the migration has not occurred on secondaries.
- Track the migration state forbidding an issuer/key writing api call if we have not migrated
- For operations that just need to read the CA bundle, use the same tracking variable to
switch between reading the legacy bundle or use the new key/issuer storage.
- Add an invalidation function that will listen for updates to our log path to refresh the state
on secondary clusters.
* Always write migration entry to trigger secondary clusters to wake up
- Some PR feedback and handle a case in which the primary cluster does
not have a CA bundle within storage but somehow a secondary does.
* Update CA Chain to report entire chain
This merges the ca_chain JSON field (of the /certs/ca_chain path) with
the regular certificate field, returning the root of trust always. This
also affects the non-JSON (raw) endpoints as well.
We return the default issuer's chain here, rather than all known issuers
(as that may not form a strict chain).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow explicit issuer override on roles
When a role is used to generate a certificate (such as with the sign/
and issue/ legacy paths or the legacy sign-verbatim/ paths), we prefer
that issuer to the one on the request. This allows operators to set an
issuer (other than default) for requests to be issued against,
effectively making the change no different from the users' perspective
as it is "just" a different role name.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for role-based issuer selection
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expand NotAfter limit enforcement behavior
Vault previously strictly enforced NotAfter/ttl values on certificate
requests, erring if the requested TTL extended past the NotAfter date of
the issuer. In the event of issuing an intermediate, this behavior was
ignored, instead permitting the issuance.
Users generally do not think to check their issuer's NotAfter date when
requesting a certificate; thus this behavior was generally surprising.
Per RFC 5280 however, issuers need to maintain status information
throughout the life cycle of the issued cert. If this leaf cert were to
be issued for a longer duration than the parent issuer, the CA must
still maintain revocation information past its expiration.
Thus, we add an option to the issuer to change the desired behavior:
- err, to err out,
- permit, to permit the longer NotAfter date, or
- truncate, to silently truncate the expiration to the issuer's
NotAfter date.
Since expiration of certificates in the system's trust store are not
generally validated (when validating an arbitrary leaf, e.g., during TLS
validation), permit should generally only be used in that case. However,
browsers usually validate intermediate's validity periods, and thus
truncate should likely be used (as with permit, the leaf's chain will
not validate towards the end of the issuance period).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add tests for expanded issuance behaviors
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add warning on keyless default issuer (#15178)
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update PKI to new Operations framework (#15180)
The backend Framework has updated Callbacks (used extensively in PKI) to
become deprecated; Operations takes their place and clarifies forwarding
of requests.
We switch to the new format everywhere, updating some bad assumptions
about forwarding along the way. Anywhere writes are handled (that should
be propagated to all nodes in all clusters), we choose to forward the
request all the way up to the performance primary cluster's primary
node. This holds for issuers/keys, roles, and configs (such as CRL
config, which is globally set for all clusters despite all clusters
having their own separate CRL).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Kitography/vault 5474 rebase (#15150)
* These parts work (put in signature so that backend wouldn't break, but missing fields, desc, etc.)
* Import and Generate API calls w/ needed additions to SDK.
* make fmt
* Add Help/Sync Text, fix some of internal/exported/kms code.
* Fix PEM/DER Encoding issue.
* make fmt
* Standardize keyIdParam, keyNameParam, keyTypeParam
* Add error response if key to be deleted is in use.
* replaces all instances of "default" in code with defaultRef
* Updates from Callbacks to Operations Function with explicit forwarding.
* Fixes a panic with names not being updated everywhere.
* add a logged error in addition to warning on deleting default key.
* Normalize whitespace upon importing keys.
Authored-by: Alexander Scheel <alexander.m.scheel@gmail.com>
* Fix isKeyInUse functionality.
* Fixes tests associated with newline at end of key pem.
* Add alternative proposal PKI aliased paths (#15211)
* Add aliased path for root/rotate/:exported
This adds a user-friendly path name for generating a rotated root. We
automatically choose the name "next" for the newly generated root at
this path if it doesn't already exist.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add aliased path for intermediate/cross-sign
This allows cross-signatures to work.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add path for replacing the current root
This updates default to point to the value of the issuer with name
"next" rather than its current value.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove plural issuers/ in signing paths
These paths use a single issuer and thus shouldn't include the plural
issuers/ as a path prefix, instead using the singular issuer/ path
prefix.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only warn if default issuer was imported
When the default issuer was not (re-)imported, we'd fail to find it,
causing an extraneous warning about missing keys, even though this
issuer indeed had a key.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing issuer sign/issue paths
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clean up various warnings within the PKI package (#15230)
* Rebuild CRLs on secondary performance clusters post migration and on new/updated issuers
- Hook into the backend invalidation function so that secondaries are notified of
new/updated issuer or migrations occuring on the primary cluster. Upon notification
schedule a CRL rebuild to take place upon the next process to read/update the CRL
or within the periodic function if no request comes in.
* Schedule rebuilding PKI CRLs on active nodes only
- Address an issue that we were scheduling the rebuilding of a CRL on standby
nodes, which would not be able to write to storage.
- Fix an issue with standby nodes not correctly determining that a migration previously
occurred.
* Return legacy CRL storage path when no migration has occurred.
* Handle issuer, keys locking (#15227)
* Handle locking of issuers during writes
We need a write lock around writes to ensure serialization of
modifications. We use a single lock for both issuer and key
updates, in part because certain operations (like deletion) will
potentially affect both.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add missing b.useLegacyBundleCaStorage guards
Several locations needed to guard against early usage of the new issuers
endpoint pre-migration.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Address PKI to properly support managed keys (#15256)
* Address codebase for managed key fixes
* Add proper public key comparison for better managed key support to importKeys
* Remove redundant public key fetching within PKI importKeys
* Correctly handle rebuilding remaining chains
When deleting a specific issuer, we might impact the chains. From a
consistency perspective, we need to ensure the remaining chains are
correct and don't refer to the since-deleted issuer, so trigger a full
rebuild here.
We don't need to call this in the delete-the-world (DELETE /root) code
path, as there shouldn't be any remaining issuers or chains to build.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove legacy CRL bundle on world deletion
When calling DELETE /root, we should remove the legacy CRL bundle, since
we're deleting the legacy CA issuer bundle as well.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Remove deleted issuers' CRL entries
Since CRLs are no longer resolvable after deletion (due to missing
issuer ID, which will cause resolution to fail regardless of if an ID or
a name/default reference was used), we should delete these CRLs from
storage to avoid leaking them.
In the event that this issuer comes back (with key material), we can
simply rebuild the CRL at that time (from the remaining revoked storage
entries).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthed JSON fetching of CRLs, Issuers (#15253)
Default to fetching JSON CRL for consistency
This makes the bare issuer-specific CRL fetching endpoint return the
JSON-wrapped CRL by default, moving the DER CRL to a specific endpoint.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add JSON-specific endpoint for fetching issuers
Unlike the unqualified /issuer/:ref endpoint (which also returns JSON),
we have a separate /issuer/:ref/json endpoint to return _only_ the
PEM-encoded certificate and the chain, mirroring the existing /cert/ca
endpoint but for a specific issuer. This allows us to make the endpoint
unauthenticated, whereas the bare endpoint would remain authenticated
and usually privileged.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Add tests for raw JSON endpoints
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add unauthenticated issuers endpoints to PKI table
This adds the unauthenticated issuers endpoints?
- LIST /issuers,
- Fetching _just_ the issuer certificates (in JSON/DER/PEM form), and
- Fetching the CRL of this issuer (in JSON/DER/PEM form).
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add issuer usage restrictions bitset
This allows issuers to have usage restrictions, limiting whether they
can be used to issue certificates or if they can generate CRLs. This
allows certain issuers to not generate a CRL (if the global config is
with the CRL enabled) or allows the issuer to not issue new certificates
(but potentially letting the CRL generation continue).
Setting both fields to false effectively forms a soft delete capability.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* PKI Pod rotation Add Base Changelog (#15283)
* PKI Pod rotation changelog.
* Use feature release-note formatting of changelog.
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
Co-authored-by: Kit Haines <kit.haines@hashicorp.com>
Co-authored-by: kitography <khaines@mit.edu>
2022-05-11 16:42:28 +00:00
|
|
|
|
|
|
|
// CreateKeyBundle create a KeyBundle struct object which includes a generated key
|
|
|
|
// of keyType with keyBits leveraging the randomness from randReader.
|
|
|
|
func CreateKeyBundle(keyType string, keyBits int, randReader io.Reader) (KeyBundle, error) {
|
|
|
|
return CreateKeyBundleWithKeyGenerator(keyType, keyBits, randReader, generatePrivateKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateKeyBundleWithKeyGenerator create a KeyBundle struct object which includes
|
|
|
|
// a generated key of keyType with keyBits leveraging the randomness from randReader and
|
|
|
|
// delegates the actual key generation to keyGenerator
|
|
|
|
func CreateKeyBundleWithKeyGenerator(keyType string, keyBits int, randReader io.Reader, keyGenerator KeyGenerator) (KeyBundle, error) {
|
|
|
|
result := KeyBundle{}
|
|
|
|
if err := keyGenerator(keyType, keyBits, &result, randReader); err != nil {
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|