From 5da2949d453de84b873344fd8d1b1f90688152b3 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 19 Feb 2016 18:50:51 -0500 Subject: [PATCH] Check role key type and bits when signing CSR. Two exceptions: signing an intermediate CA CSR, and signing a CSR via the 'sign-verbatim' path. --- builtin/logical/pki/cert_util.go | 57 +++++++++++++++++++++++++- builtin/logical/pki/path_issue_sign.go | 1 + builtin/logical/pki/path_root.go | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index 406664331b5e..e155422506af 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -1,6 +1,7 @@ package pki import ( + "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/sha1" @@ -437,7 +438,60 @@ func signCert(b *backend, return nil, certutil.UserError{Err: "certificate request could not be parsed"} } - if csr.PublicKeyAlgorithm == x509.RSA { + switch role.KeyType { + case "rsa": + // Verify that the key matches the role type + if csr.PublicKeyAlgorithm != x509.RSA { + return nil, certutil.UserError{Err: fmt.Sprintf( + "role requires keys of type %s", + role.KeyType)} + } + pubKey, ok := csr.PublicKey.(*rsa.PublicKey) + if !ok { + return nil, certutil.UserError{Err: "could not parse CSR's public key"} + } + + // Verify that the key is at least 2048 bits + if pubKey.N.BitLen() < 2048 { + return nil, certutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} + } + + // Verify that the bit size is at least the size specified in the role + if pubKey.N.BitLen() < role.KeyBits { + return nil, certutil.UserError{Err: fmt.Sprintf( + "role requires a minimum of a %d-bit key, but CSR's key is %d bits", + role.KeyBits, + pubKey.N.BitLen())} + } + + case "ec": + // Verify that the key matches the role type + if csr.PublicKeyAlgorithm != x509.ECDSA { + return nil, certutil.UserError{Err: fmt.Sprintf( + "role requires keys of type %s", + role.KeyType)} + } + pubKey, ok := csr.PublicKey.(*ecdsa.PublicKey) + if !ok { + return nil, certutil.UserError{Err: "could not parse CSR's public key"} + } + + // Verify that the bit size is at least the size specified in the role + if pubKey.Params().BitSize < role.KeyBits { + return nil, certutil.UserError{Err: fmt.Sprintf( + "role requires a minimum of a %d-bit key, but CSR's key is %d bits", + role.KeyBits, + pubKey.Params().BitSize)} + } + + case "any": + // We only care about running RSA < 2048 bit checks, so if not RSA + // break out + if csr.PublicKeyAlgorithm != x509.RSA { + break + } + + // Run RSA < 2048 bit checks pubKey, ok := csr.PublicKey.(*rsa.PublicKey) if !ok { return nil, certutil.UserError{Err: "could not parse CSR's public key"} @@ -445,6 +499,7 @@ func signCert(b *backend, if pubKey.N.BitLen() < 2048 { return nil, certutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} } + } creationBundle, err := generateCreationBundle(b, role, signingBundle, csr, req, data) diff --git a/builtin/logical/pki/path_issue_sign.go b/builtin/logical/pki/path_issue_sign.go index c85dd8744e29..9d523ffcc043 100644 --- a/builtin/logical/pki/path_issue_sign.go +++ b/builtin/logical/pki/path_issue_sign.go @@ -123,6 +123,7 @@ func (b *backend) pathSignVerbatim( AllowAnyName: true, AllowIPSANs: true, EnforceHostnames: false, + KeyType: "any", } return b.pathIssueSignCert(req, data, role, true, true) diff --git a/builtin/logical/pki/path_root.go b/builtin/logical/pki/path_root.go index 6bc14d535602..d82dc8e2c450 100644 --- a/builtin/logical/pki/path_root.go +++ b/builtin/logical/pki/path_root.go @@ -196,6 +196,7 @@ func (b *backend) pathCASignIntermediate( AllowAnyName: true, AllowIPSANs: true, EnforceHostnames: false, + KeyType: "any", } if cn := data.Get("common_name").(string); len(cn) == 0 {