mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
Set some basic key usages by default.
Some programs (such as OpenVPN) don't like it if you don't include key usages. This adds a default set that should suffice for most extended usages. However, since things get twitchy when these are set in ways various crypto stacks don't like, it's fully controllable by the user. Fixes #1476
This commit is contained in:
@@ -29,8 +29,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
stepCount = 0
|
||||
serialUnderTest string
|
||||
stepCount = 0
|
||||
serialUnderTest string
|
||||
parsedKeyUsageUnderTest int
|
||||
)
|
||||
|
||||
// Performs basic tests on CA functionality
|
||||
@@ -366,7 +367,7 @@ func TestBackend_ECRoles_CSR(t *testing.T) {
|
||||
}
|
||||
|
||||
// Performs some validity checking on the returned bundles
|
||||
func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage certUsage, validity time.Duration, certBundle *certutil.CertBundle) (*certutil.ParsedCertBundle, error) {
|
||||
func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage x509.KeyUsage, extUsage x509.ExtKeyUsage, validity time.Duration, certBundle *certutil.CertBundle) (*certutil.ParsedCertBundle, error) {
|
||||
parsedCertBundle, err := certBundle.ToParsedCertBundle()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing cert bundle: %s", err)
|
||||
@@ -407,27 +408,32 @@ func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage certUsage,
|
||||
}
|
||||
|
||||
cert := parsedCertBundle.Certificate
|
||||
// There should only be one usage type, because only one is requested
|
||||
|
||||
if usage != cert.KeyUsage {
|
||||
return nil, fmt.Errorf("Expected usage of %#v, got %#v; ext usage is %#v", usage, cert.KeyUsage, cert.ExtKeyUsage)
|
||||
}
|
||||
|
||||
// There should only be one ext usage type, because only one is requested
|
||||
// in the tests
|
||||
if len(cert.ExtKeyUsage) != 1 {
|
||||
return nil, fmt.Errorf("Got wrong size key usage in generated cert; values are %#v", cert.ExtKeyUsage)
|
||||
}
|
||||
switch usage {
|
||||
case emailProtectionUsage:
|
||||
switch extUsage {
|
||||
case x509.ExtKeyUsageEmailProtection:
|
||||
if cert.ExtKeyUsage[0] != x509.ExtKeyUsageEmailProtection {
|
||||
return nil, fmt.Errorf("Bad key usage")
|
||||
return nil, fmt.Errorf("Bad extended key usage")
|
||||
}
|
||||
case serverUsage:
|
||||
case x509.ExtKeyUsageServerAuth:
|
||||
if cert.ExtKeyUsage[0] != x509.ExtKeyUsageServerAuth {
|
||||
return nil, fmt.Errorf("Bad key usage")
|
||||
return nil, fmt.Errorf("Bad extended key usage")
|
||||
}
|
||||
case clientUsage:
|
||||
case x509.ExtKeyUsageClientAuth:
|
||||
if cert.ExtKeyUsage[0] != x509.ExtKeyUsageClientAuth {
|
||||
return nil, fmt.Errorf("Bad key usage")
|
||||
return nil, fmt.Errorf("Bad extended key usage")
|
||||
}
|
||||
case codeSigningUsage:
|
||||
case x509.ExtKeyUsageCodeSigning:
|
||||
if cert.ExtKeyUsage[0] != x509.ExtKeyUsageCodeSigning {
|
||||
return nil, fmt.Errorf("Bad key usage")
|
||||
return nil, fmt.Errorf("Bad extended key usage")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1497,14 +1503,14 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
|
||||
|
||||
// Returns a TestCheckFunc that performs various validity checks on the
|
||||
// returned certificate information, mostly within checkCertsAndPrivateKey
|
||||
getCnCheck := func(name string, role roleEntry, key crypto.Signer, usage certUsage, validity time.Duration) logicaltest.TestCheckFunc {
|
||||
getCnCheck := func(name string, role roleEntry, key crypto.Signer, usage x509.KeyUsage, extUsage x509.ExtKeyUsage, validity time.Duration) logicaltest.TestCheckFunc {
|
||||
var certBundle certutil.CertBundle
|
||||
return func(resp *logical.Response) error {
|
||||
err := mapstructure.Decode(resp.Data, &certBundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parsedCertBundle, err := checkCertsAndPrivateKey(role.KeyType, key, usage, validity, &certBundle)
|
||||
parsedCertBundle, err := checkCertsAndPrivateKey(role.KeyType, key, usage, extUsage, validity, &certBundle)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error checking generated certificate: %s", err)
|
||||
}
|
||||
@@ -1567,20 +1573,54 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
|
||||
roleVals.ClientFlag = false
|
||||
roleVals.CodeSigningFlag = false
|
||||
roleVals.EmailProtectionFlag = false
|
||||
var usage certUsage
|
||||
i := mathRand.Int()
|
||||
|
||||
var usage string
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",DigitalSignature"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",ContentCoMmitment"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",KeyEncipherment"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",DataEncipherment"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",KeyAgreemEnt"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",CertSign"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",CRLSign"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",EncipherOnly"
|
||||
}
|
||||
if mathRand.Int()%2 == 1 {
|
||||
usage = usage + ",DecipherOnly"
|
||||
}
|
||||
|
||||
roleVals.KeyUsage = usage
|
||||
parsedKeyUsage := parseKeyUsages(roleVals.KeyUsage)
|
||||
parsedKeyUsageUnderTest = parsedKeyUsage
|
||||
|
||||
var extUsage x509.ExtKeyUsage
|
||||
i := mathRand.Int() % 4
|
||||
switch {
|
||||
case i%5 == 0:
|
||||
usage = emailProtectionUsage
|
||||
case i == 0:
|
||||
extUsage = x509.ExtKeyUsageEmailProtection
|
||||
roleVals.EmailProtectionFlag = true
|
||||
case i%3 == 0:
|
||||
usage = serverUsage
|
||||
case i == 1:
|
||||
extUsage = x509.ExtKeyUsageServerAuth
|
||||
roleVals.ServerFlag = true
|
||||
case i%2 == 0:
|
||||
usage = clientUsage
|
||||
case i == 2:
|
||||
extUsage = x509.ExtKeyUsageClientAuth
|
||||
roleVals.ClientFlag = true
|
||||
default:
|
||||
usage = codeSigningUsage
|
||||
extUsage = x509.ExtKeyUsageCodeSigning
|
||||
roleVals.CodeSigningFlag = true
|
||||
}
|
||||
|
||||
@@ -1666,9 +1706,9 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
|
||||
}
|
||||
issueVals.CSR = strings.TrimSpace(string(pem.EncodeToMemory(&block)))
|
||||
|
||||
addTests(getCnCheck(issueVals.CommonName, roleVals, privKey, usage, validity))
|
||||
addTests(getCnCheck(issueVals.CommonName, roleVals, privKey, x509.KeyUsage(parsedKeyUsage), extUsage, validity))
|
||||
} else {
|
||||
addTests(getCnCheck(issueVals.CommonName, roleVals, nil, usage, validity))
|
||||
addTests(getCnCheck(issueVals.CommonName, roleVals, nil, x509.KeyUsage(parsedKeyUsage), extUsage, validity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,13 @@ import (
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
type certUsage int
|
||||
type certExtKeyUsage int
|
||||
|
||||
const (
|
||||
serverExtUsage certUsage = 1 << iota
|
||||
clientExtUsage
|
||||
codeSigningExtUsage
|
||||
emailProtectionExtUsage
|
||||
caUsage
|
||||
serverExtKeyUsage certExtKeyUsage = 1 << iota
|
||||
clientExtKeyUsage
|
||||
codeSigningExtKeyUsage
|
||||
emailProtectionExtKeyUsage
|
||||
)
|
||||
|
||||
type creationBundle struct {
|
||||
@@ -40,7 +39,8 @@ type creationBundle struct {
|
||||
KeyBits int
|
||||
SigningBundle *caInfoBundle
|
||||
TTL time.Duration
|
||||
Usage certUsage
|
||||
KeyUsage x509.KeyUsage
|
||||
ExtKeyUsage certExtKeyUsage
|
||||
|
||||
// Only used when signing a CA cert
|
||||
UseCSRValues bool
|
||||
@@ -679,19 +679,19 @@ func generateCreationBundle(b *backend,
|
||||
}
|
||||
|
||||
// Build up usages
|
||||
var usage certUsage
|
||||
var extUsage certExtKeyUsage
|
||||
{
|
||||
if role.ServerFlag {
|
||||
usage = usage | serverExtUsage
|
||||
extUsage = extUsage | serverExtKeyUsage
|
||||
}
|
||||
if role.ClientFlag {
|
||||
usage = usage | clientExtUsage
|
||||
extUsage = extUsage | clientExtKeyUsage
|
||||
}
|
||||
if role.CodeSigningFlag {
|
||||
usage = usage | codeSigningExtUsage
|
||||
extUsage = extUsage | codeSigningExtKeyUsage
|
||||
}
|
||||
if role.EmailProtectionFlag {
|
||||
usage = usage | emailProtectionExtUsage
|
||||
extUsage = extUsage | emailProtectionExtKeyUsage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,7 +704,8 @@ func generateCreationBundle(b *backend,
|
||||
KeyBits: role.KeyBits,
|
||||
SigningBundle: signingBundle,
|
||||
TTL: ttl,
|
||||
Usage: usage,
|
||||
KeyUsage: x509.KeyUsage(role.ParsedKeyUsage),
|
||||
ExtKeyUsage: extUsage,
|
||||
}
|
||||
|
||||
// Don't deal with URLs or max path length if it's self-signed, as these
|
||||
@@ -747,16 +748,18 @@ func addKeyUsages(creationInfo *creationBundle, certTemplate *x509.Certificate)
|
||||
return
|
||||
}
|
||||
|
||||
if creationInfo.Usage&serverExtUsage != 0 {
|
||||
certTemplate.KeyUsage = creationInfo.KeyUsage
|
||||
|
||||
if creationInfo.ExtKeyUsage&serverExtKeyUsage != 0 {
|
||||
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
|
||||
}
|
||||
if creationInfo.Usage&clientExtUsage != 0 {
|
||||
if creationInfo.ExtKeyUsage&clientExtKeyUsage != 0 {
|
||||
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
|
||||
}
|
||||
if creationInfo.Usage&codeSigningExtUsage != 0 {
|
||||
if creationInfo.ExtKeyUsage&codeSigningExtKeyUsage != 0 {
|
||||
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
|
||||
}
|
||||
if creationInfo.Usage&emailProtectionExtUsage != 0 {
|
||||
if creationInfo.ExtKeyUsage&emailProtectionExtKeyUsage != 0 {
|
||||
certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package pki
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -147,6 +148,17 @@ certainly want to change this if you adjust
|
||||
the key_type.`,
|
||||
},
|
||||
|
||||
"key_usage": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "DigitalSignature,KeyAgreement,KeyEncipherment",
|
||||
Description: `A comma-separated set of key usages (not extended
|
||||
key usages). Valid values can be found at
|
||||
https://golang.org/pkg/crypto/x509/#KeyUsage
|
||||
-- simply drop the "KeyUsage" part of the name.
|
||||
To remove all key usages from being set, set
|
||||
this value to an empty string.`,
|
||||
},
|
||||
|
||||
"use_csr_common_name": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Default: true,
|
||||
@@ -315,6 +327,7 @@ func (b *backend) pathRoleCreate(
|
||||
KeyType: data.Get("key_type").(string),
|
||||
KeyBits: data.Get("key_bits").(int),
|
||||
UseCSRCommonName: data.Get("use_csr_common_name").(bool),
|
||||
KeyUsage: data.Get("key_usage").(string),
|
||||
}
|
||||
|
||||
if entry.KeyType == "rsa" && entry.KeyBits < 2048 {
|
||||
@@ -364,6 +377,9 @@ func (b *backend) pathRoleCreate(
|
||||
return errResp, nil
|
||||
}
|
||||
|
||||
// Parse key usages
|
||||
entry.ParsedKeyUsage = parseKeyUsages(entry.KeyUsage)
|
||||
|
||||
// Store it
|
||||
jsonEntry, err := logical.StorageEntryJSON("role/"+name, entry)
|
||||
if err != nil {
|
||||
@@ -376,6 +392,35 @@ func (b *backend) pathRoleCreate(
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func parseKeyUsages(input string) int {
|
||||
var parsedKeyUsages x509.KeyUsage
|
||||
splitKeyUsage := strings.Split(input, ",")
|
||||
for _, k := range splitKeyUsage {
|
||||
switch strings.ToLower(k) {
|
||||
case "digitalsignature":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageDigitalSignature
|
||||
case "contentcommitment":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageContentCommitment
|
||||
case "keyencipherment":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageKeyEncipherment
|
||||
case "dataencipherment":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageDataEncipherment
|
||||
case "keyagreement":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageKeyAgreement
|
||||
case "certsign":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageCertSign
|
||||
case "crlsign":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageCRLSign
|
||||
case "encipheronly":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageEncipherOnly
|
||||
case "decipheronly":
|
||||
parsedKeyUsages = parsedKeyUsages | x509.KeyUsageDecipherOnly
|
||||
}
|
||||
}
|
||||
|
||||
return int(parsedKeyUsages)
|
||||
}
|
||||
|
||||
type roleEntry struct {
|
||||
LeaseMax string `json:"lease_max" structs:"lease_max" mapstructure:"lease_max"`
|
||||
Lease string `json:"lease" structs:"lease" mapstructure:"lease"`
|
||||
@@ -399,6 +444,8 @@ type roleEntry struct {
|
||||
KeyType string `json:"key_type" structs:"key_type" mapstructure:"key_type"`
|
||||
KeyBits int `json:"key_bits" structs:"key_bits" mapstructure:"key_bits"`
|
||||
MaxPathLength *int `json:",omitempty" structs:",omitempty"`
|
||||
KeyUsage string `json:"key_usage" structs:"key_usage" mapstructure:"key_usage"`
|
||||
ParsedKeyUsage int `json:"parsed_key_usage" structs:"parsed_key_usage" mapstructure:"parsed_key_usage"`
|
||||
}
|
||||
|
||||
const pathListRolesHelpSyn = `List the existing roles in this backend`
|
||||
|
||||
Reference in New Issue
Block a user