From f6bdda8c9c636e228ed5212ea4964254d9600c72 Mon Sep 17 00:00:00 2001 From: Hridoy Roy Date: Fri, 8 Jan 2021 09:48:27 -0800 Subject: [PATCH] add variable entropy readers to cert gen helpers [VAULT-1179] (#10653) * move entropy augmentation in cert gen to oss * changelog * go mod vendor * updated helpers to allow custom entropy * comments * comments --- changelog/10653.txt | 3 + sdk/helper/certutil/helpers.go | 94 +++++++++++++++---- .../vault/sdk/helper/certutil/helpers.go | 94 +++++++++++++++---- 3 files changed, 157 insertions(+), 34 deletions(-) create mode 100644 changelog/10653.txt diff --git a/changelog/10653.txt b/changelog/10653.txt new file mode 100644 index 000000000..16bf22d53 --- /dev/null +++ b/changelog/10653.txt @@ -0,0 +1,3 @@ +```release-note:feature +sdk: Private key generation in the certutil package now allows custom io.Readers to be used. +``` \ No newline at end of file diff --git a/sdk/helper/certutil/helpers.go b/sdk/helper/certutil/helpers.go index ecade6682..12198798e 100644 --- a/sdk/helper/certutil/helpers.go +++ b/sdk/helper/certutil/helpers.go @@ -191,17 +191,34 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { return parsedBundle, nil } -// GeneratePrivateKey generates a private key with the specified type and key bits +// GeneratePrivateKey generates a private key with the specified type and key bits. func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyContainer) error { + 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 { var err error var privateKeyType PrivateKeyType var privateKeyBytes []byte var privateKey crypto.Signer + var randReader io.Reader = rand.Reader + if entropyReader != nil { + randReader = entropyReader + } + switch keyType { case "rsa": privateKeyType = RSAPrivateKey - privateKey, err = rsa.GenerateKey(rand.Reader, keyBits) + privateKey, err = rsa.GenerateKey(randReader, keyBits) if err != nil { return errutil.InternalError{Err: fmt.Sprintf("error generating RSA private key: %v", err)} } @@ -221,7 +238,7 @@ func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyC default: return errutil.UserError{Err: fmt.Sprintf("unsupported bit length for EC key: %d", keyBits)} } - privateKey, err = ecdsa.GenerateKey(curve, rand.Reader) + privateKey, err = ecdsa.GenerateKey(curve, randReader) if err != nil { return errutil.InternalError{Err: fmt.Sprintf("error generating EC private key: %v", err)} } @@ -239,7 +256,17 @@ func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyC // GenerateSerialNumber generates a serial number suitable for a certificate func GenerateSerialNumber() (*big.Int, error) { - serial, err := rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil)) + 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)) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("error generating serial number: %v", err)} } @@ -491,9 +518,19 @@ func ValidateKeyTypeLength(keyType string, keyBits int) error { return nil } -// Performs the heavy lifting of creating a certificate. Returns -// a fully-filled-in ParsedCertBundle. +// CreateCertificate uses CreationBundle and the default rand.Reader to +// generate a cert/keypair. func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { + return createCertificate(data, rand.Reader) +} + +// CreateCertificateWithRandomSource uses CreationBundle and a custom +// io.Reader for randomness to generate a cert/keypair. +func CreateCertificateWithRandomSource(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) { + return createCertificate(data, randReader) +} + +func createCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) { var err error result := &ParsedCertBundle{} @@ -502,9 +539,9 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { return nil, err } - if err := GeneratePrivateKey(data.Params.KeyType, + if err := generatePrivateKey(data.Params.KeyType, data.Params.KeyBits, - result); err != nil { + result, randReader); err != nil { return nil, err } @@ -569,7 +606,7 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { caCert := data.SigningBundle.Certificate certTemplate.AuthorityKeyId = caCert.SubjectKeyId - certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, result.PrivateKey.Public(), data.SigningBundle.PrivateKey) + certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, result.PrivateKey.Public(), data.SigningBundle.PrivateKey) } else { // Creating a self-signed root if data.Params.MaxPathLength == 0 { @@ -588,7 +625,7 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { certTemplate.AuthorityKeyId = subjKeyID certTemplate.BasicConstraintsValid = true - certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey) + certBytes, err = x509.CreateCertificate(randReader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey) } if err != nil { @@ -620,15 +657,26 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { var oidExtensionBasicConstraints = []int{2, 5, 29, 19} -// Creates a CSR. This is currently only meant for use when -// generating an intermediate certificate. +// 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. func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle, error) { + return createCSR(data, addBasicConstraints, rand.Reader) +} + +// 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) { + return createCSR(data, addBasicConstraints, randReader) +} + +func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Reader) (*ParsedCSRBundle, error) { var err error result := &ParsedCSRBundle{} - if err := GeneratePrivateKey(data.Params.KeyType, + if err := generatePrivateKey(data.Params.KeyType, data.Params.KeyBits, - result); err != nil { + result, randReader); err != nil { return nil, err } @@ -669,7 +717,7 @@ func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle csrTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256 } - csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, result.PrivateKey) + csr, err := x509.CreateCertificateRequest(randReader, csrTemplate, result.PrivateKey) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)} } @@ -683,9 +731,21 @@ func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle return result, nil } -// Performs the heavy lifting of generating a certificate from a CSR. +// SignCertificate performs the heavy lifting +// of generating a certificate from a CSR. // Returns a ParsedCertBundle sans private keys. func SignCertificate(data *CreationBundle) (*ParsedCertBundle, error) { + 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) { switch { case data == nil: return nil, errutil.UserError{Err: "nil data bundle given to signCertificate"} @@ -797,7 +857,7 @@ func SignCertificate(data *CreationBundle) (*ParsedCertBundle, error) { certTemplate.PermittedDNSDomainsCritical = true } - certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, data.CSR.PublicKey, data.SigningBundle.PrivateKey) + certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, data.CSR.PublicKey, data.SigningBundle.PrivateKey) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)} diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go b/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go index ecade6682..12198798e 100644 --- a/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go +++ b/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go @@ -191,17 +191,34 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { return parsedBundle, nil } -// GeneratePrivateKey generates a private key with the specified type and key bits +// GeneratePrivateKey generates a private key with the specified type and key bits. func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyContainer) error { + 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 { var err error var privateKeyType PrivateKeyType var privateKeyBytes []byte var privateKey crypto.Signer + var randReader io.Reader = rand.Reader + if entropyReader != nil { + randReader = entropyReader + } + switch keyType { case "rsa": privateKeyType = RSAPrivateKey - privateKey, err = rsa.GenerateKey(rand.Reader, keyBits) + privateKey, err = rsa.GenerateKey(randReader, keyBits) if err != nil { return errutil.InternalError{Err: fmt.Sprintf("error generating RSA private key: %v", err)} } @@ -221,7 +238,7 @@ func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyC default: return errutil.UserError{Err: fmt.Sprintf("unsupported bit length for EC key: %d", keyBits)} } - privateKey, err = ecdsa.GenerateKey(curve, rand.Reader) + privateKey, err = ecdsa.GenerateKey(curve, randReader) if err != nil { return errutil.InternalError{Err: fmt.Sprintf("error generating EC private key: %v", err)} } @@ -239,7 +256,17 @@ func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyC // GenerateSerialNumber generates a serial number suitable for a certificate func GenerateSerialNumber() (*big.Int, error) { - serial, err := rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil)) + 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)) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("error generating serial number: %v", err)} } @@ -491,9 +518,19 @@ func ValidateKeyTypeLength(keyType string, keyBits int) error { return nil } -// Performs the heavy lifting of creating a certificate. Returns -// a fully-filled-in ParsedCertBundle. +// CreateCertificate uses CreationBundle and the default rand.Reader to +// generate a cert/keypair. func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { + return createCertificate(data, rand.Reader) +} + +// CreateCertificateWithRandomSource uses CreationBundle and a custom +// io.Reader for randomness to generate a cert/keypair. +func CreateCertificateWithRandomSource(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) { + return createCertificate(data, randReader) +} + +func createCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) { var err error result := &ParsedCertBundle{} @@ -502,9 +539,9 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { return nil, err } - if err := GeneratePrivateKey(data.Params.KeyType, + if err := generatePrivateKey(data.Params.KeyType, data.Params.KeyBits, - result); err != nil { + result, randReader); err != nil { return nil, err } @@ -569,7 +606,7 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { caCert := data.SigningBundle.Certificate certTemplate.AuthorityKeyId = caCert.SubjectKeyId - certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, result.PrivateKey.Public(), data.SigningBundle.PrivateKey) + certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, result.PrivateKey.Public(), data.SigningBundle.PrivateKey) } else { // Creating a self-signed root if data.Params.MaxPathLength == 0 { @@ -588,7 +625,7 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { certTemplate.AuthorityKeyId = subjKeyID certTemplate.BasicConstraintsValid = true - certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey) + certBytes, err = x509.CreateCertificate(randReader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey) } if err != nil { @@ -620,15 +657,26 @@ func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) { var oidExtensionBasicConstraints = []int{2, 5, 29, 19} -// Creates a CSR. This is currently only meant for use when -// generating an intermediate certificate. +// 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. func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle, error) { + return createCSR(data, addBasicConstraints, rand.Reader) +} + +// 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) { + return createCSR(data, addBasicConstraints, randReader) +} + +func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Reader) (*ParsedCSRBundle, error) { var err error result := &ParsedCSRBundle{} - if err := GeneratePrivateKey(data.Params.KeyType, + if err := generatePrivateKey(data.Params.KeyType, data.Params.KeyBits, - result); err != nil { + result, randReader); err != nil { return nil, err } @@ -669,7 +717,7 @@ func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle csrTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256 } - csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, result.PrivateKey) + csr, err := x509.CreateCertificateRequest(randReader, csrTemplate, result.PrivateKey) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)} } @@ -683,9 +731,21 @@ func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle return result, nil } -// Performs the heavy lifting of generating a certificate from a CSR. +// SignCertificate performs the heavy lifting +// of generating a certificate from a CSR. // Returns a ParsedCertBundle sans private keys. func SignCertificate(data *CreationBundle) (*ParsedCertBundle, error) { + 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) { switch { case data == nil: return nil, errutil.UserError{Err: "nil data bundle given to signCertificate"} @@ -797,7 +857,7 @@ func SignCertificate(data *CreationBundle) (*ParsedCertBundle, error) { certTemplate.PermittedDNSDomainsCritical = true } - certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, data.CSR.PublicKey, data.SigningBundle.PrivateKey) + certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, data.CSR.PublicKey, data.SigningBundle.PrivateKey) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}