Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add variable entropy readers to cert gen helpers [VAULT-1179] #10653

Merged
merged 7 commits into from
Jan 8, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/10653.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
sdk: Private key generation in the certutil package now allows custom io.Readers to be used.
```
94 changes: 77 additions & 17 deletions sdk/helper/certutil/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
}
Expand All @@ -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)}
}
Expand All @@ -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)}
}
Expand Down Expand Up @@ -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{}

Expand All @@ -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
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)}
}
Expand All @@ -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"}
Expand Down Expand Up @@ -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)}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.