Skip to content

Commit

Permalink
csr-verbatim support
Browse files Browse the repository at this point in the history
  • Loading branch information
tlm committed Nov 25, 2018
1 parent 406fa07 commit d417aae
Show file tree
Hide file tree
Showing 15 changed files with 356 additions and 44 deletions.
20 changes: 12 additions & 8 deletions config/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ type AltNamesConfig struct {
}

type AnchorConfig struct {
Action ActionConfig
AltNames AltNamesConfig
CommonName string
CN string
Dest string
DestOpts map[string]interface{}
Name string
Provider string
Action ActionConfig
AltNames AltNamesConfig
CommonName string
CN string
Dest string
DestOpts map[string]interface{}
Name string
Organization []string
OrganizationalUnit []string
Provider string
}

type ProviderFetcher func(string) (provider.Provider, bool)
Expand Down Expand Up @@ -56,6 +58,8 @@ func GetMembers(v *viper.Viper, pFetcher ProviderFetcher) ([]conductor.Member, e
DNSNames: anchor.AltNames.DNSNames,
IPAddresses: anchor.AltNames.IPAddresses,
},
Organization: anchor.Organization,
OrganizationalUnit: anchor.OrganizationalUnit,
}

if anchor.CN != "" {
Expand Down
5 changes: 5 additions & 0 deletions config/member_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package config

import (
_ "testing"
)
8 changes: 5 additions & 3 deletions provider/request.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package provider

type AltNames struct {
EmailAddresses []string
DNSNames []string
EmailAddresses []string
IPAddresses []string
}

type Request struct {
CommonName string
AltNames AltNames
AltNames AltNames
CommonName string
Organization []string
OrganizationalUnit []string
}
55 changes: 55 additions & 0 deletions request/csr_requester.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package request

import (
"crypto/rand"
"crypto/x509"
"fmt"
"net"

"github.com/pkg/errors"

"github.com/tlmiller/disttrust/provider"
)

type CSRRequester struct {
KeyMaker KeyMaker
}

func (c *CSRRequester) CSRFromRequest(req *provider.Request) (
[]byte, Key, error) {

csrTmpl := x509.CertificateRequest{}
csrTmpl.Subject.CommonName = req.CommonName
csrTmpl.Subject.Organization = req.Organization
csrTmpl.Subject.OrganizationalUnit = req.OrganizationalUnit
csrTmpl.DNSNames = req.AltNames.DNSNames
csrTmpl.EmailAddresses = req.AltNames.EmailAddresses

for _, ipStr := range req.AltNames.IPAddresses {
ip := net.ParseIP(ipStr)
if ip == nil {
return nil, nil, fmt.Errorf("ip address %s is not valid", ipStr)
}
csrTmpl.IPAddresses = append(csrTmpl.IPAddresses, ip)
}

key, err := c.KeyMaker.MakeKey()
if err != nil {
return nil, nil, errors.Wrapf(err, "making csr private key for %s",
req.CommonName)
}

csrData, err := x509.CreateCertificateRequest(rand.Reader, &csrTmpl, key.Raw())
if err != nil {
return nil, nil, errors.Wrapf(err, "creating certificate request for %s",
req.CommonName)
}

return csrData, key, nil
}

func NewCSRRequester(keyMaker KeyMaker) *CSRRequester {
return &CSRRequester{
KeyMaker: keyMaker,
}
}
50 changes: 50 additions & 0 deletions request/csr_requester_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package request

import (
"crypto/x509"
"testing"

"github.com/tlmiller/disttrust/provider"
"github.com/tlmiller/disttrust/util"
)

func TestCSRFromRequestResult(t *testing.T) {
req := provider.Request{
CommonName: "example.com",
AltNames: provider.AltNames{
DNSNames: []string{"www.example.com"},
EmailAddresses: []string{"test@example.com"},
IPAddresses: []string{"fe80:aabb::1"},
},
}
csrr := NewCSRRequester(NewRSAKeyMaker(1024))
csrData, err := csrr.CSRFromRequest(&req)
if err != nil {
t.Fatalf("unexpected error generating csr from request: %v", err)
}

csr, err := x509.ParseCertificateRequest(csrData)
if err != nil {
t.Fatalf("unexpected error building csr represnetation: %v", err)
}

if csr.Subject.CommonName != "example.com" {
t.Errorf("generated csr common name, expected example.com got %s",
csr.Subject.CommonName)
}

if len(csr.DNSNames) != 1 && !util.StringInSlice("www.example.com", csr.DNSNames) {
t.Errorf("generated csr dns names, expected [www.example.com] got %v",
csr.DNSNames)
}

if len(csr.EmailAddresses) != 1 && !util.StringInSlice(
"test@example.com", csr.EmailAddresses) {
t.Errorf("generated csr email addresses, expected [test@example.com] got %v",
csr.EmailAddresses)
}

if len(csr.IPAddresses) != 1 {
t.Error("expected 1 ip address in generated csr")
}
}
10 changes: 10 additions & 0 deletions request/key_maker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package request

type Key interface {
PKCS8() ([]byte, error)
Raw() interface{}
}

type KeyMaker interface {
MakeKey() (Key, error)
}
36 changes: 36 additions & 0 deletions request/rsa_key_maker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package request

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
)

type rsaKey struct {
key *rsa.PrivateKey
}

type RSAKeyMaker struct {
Bits int
}

func (r *RSAKeyMaker) MakeKey() (Key, error) {
k, err := rsa.GenerateKey(rand.Reader, r.Bits)
return &rsaKey{
key: k,
}, err
}

func NewRSAKeyMaker(bits int) *RSAKeyMaker {
return &RSAKeyMaker{
Bits: bits,
}
}

func (k *rsaKey) PKCS8() ([]byte, error) {
return x509.MarshalPKCS8PrivateKey(k.key)
}

func (k *rsaKey) Raw() interface{} {
return k.key
}
13 changes: 13 additions & 0 deletions request/rsa_key_maker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package request

import (
"testing"
)

func TestRSAKeyGenMeta(t *testing.T) {
maker := NewRSAKeyMaker(4096)
_, err := maker.MakeKey()
if err != nil {
t.Fatalf("unexpected error making new rsa key: %v", err)
}
}
69 changes: 53 additions & 16 deletions vault/config/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import (
_ "github.com/spf13/viper"

"github.com/tlmiller/disttrust/provider"
"github.com/tlmiller/disttrust/request"
"github.com/tlmiller/disttrust/util"
"github.com/tlmiller/disttrust/vault"
)

type RequestConfig struct {
CSR bool
RollKey bool
CSR bool
KeyType string
RSABits int
ECDSACurve string
}

type VaultConfig struct {
Expand All @@ -25,12 +29,27 @@ type VaultConfig struct {
Role string
}

const (
RSAKeyType = "rsa"
)

var (
DefaultRequestConfig = RequestConfig{
CSR: false,
KeyType: "rsa",
RSABits: 2048,
ECDSACurve: "p246",
}
)

var (
SupportedCurves = []string{"p224", "p256", "p384", "p521"}
SupportedKeyTypes = []string{"rsa", "ecdsa"}
)

func New() interface{} {
return &VaultConfig{
Request: RequestConfig{
CSR: false,
RollKey: false,
},
Request: DefaultRequestConfig,
}
}

Expand All @@ -40,6 +59,33 @@ func Mapper(v interface{}) (provider.Provider, error) {
return nil, errors.New("parsing vault provider config")
}

if conf.Path == "" {
return nil, errors.New("no vault pki path specificed")
}

if conf.Role == "" {
return nil, errors.New("no vault pki role for path specified")
}

var issuer vault.Issuer
if conf.Request.CSR {
if !util.StringInSlice(conf.Request.KeyType, SupportedKeyTypes) {
return nil, fmt.Errorf(
"request key type must be one of %v for vault csr requests",
SupportedKeyTypes)
}

var keyMaker request.KeyMaker
if conf.Request.KeyType == RSAKeyType {
keyMaker = request.NewRSAKeyMaker(conf.Request.RSABits)
}

requester := request.NewCSRRequester(keyMaker)
issuer = vault.CSRVerbatimIssuer(conf.Path, conf.Role, requester)
} else {
issuer = vault.GenerateIssuer(conf.Path, conf.Role)
}

authMaker, exists := vault.AuthHandlers[conf.AuthMethod]
if !exists {
return nil, fmt.Errorf("no auth handler for method '%s'", conf.AuthMethod)
Expand All @@ -52,16 +98,7 @@ func Mapper(v interface{}) (provider.Provider, error) {
pconfig := vault.Config{}
pconfig.Address = conf.Address

if conf.Path == "" {
return nil, errors.New("no vault pki path specificed")
}
pconfig.Path = conf.Path
if conf.Role == "" {
return nil, errors.New("no vault pki role for path specified")
}
pconfig.Role = conf.Role

provider, err := vault.NewProvider(pconfig, auth)
provider, err := vault.NewProvider(pconfig, issuer, auth)
if err != nil {
return nil, errors.Wrap(err, "building vault provider from config")
}
Expand Down
59 changes: 59 additions & 0 deletions vault/csr_verbatim_issuer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package vault

import (
"encoding/pem"
"fmt"

"github.com/pkg/errors"

"github.com/tlmiller/disttrust/provider"
"github.com/tlmiller/disttrust/request"
)

const (
KeyCSR = "csr"
)

func CSRVerbatimIssuer(path, role string, requester *request.CSRRequester) Issuer {
return IssuerFunc(func(r *provider.Request, w Writer) (provider.Lease, error) {
csrASN1, key, err := requester.CSRFromRequest(r)
if err != nil {
return nil, errors.Wrapf(err, "making vault sign csr for %s",
r.CommonName)
}

csrPEMRaw := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csrASN1,
})
dest := fmt.Sprintf("%s/sign-verbatim/%s", path, role)
data := map[string]interface{}{}
data[KeyCSR] = string(csrPEMRaw)
data[KeyFormat] = "pem"

secret, err := w.Write(dest, data)
if err != nil {
return nil, errors.Wrapf(err,
"signing certificate verbatim from vault for %s", r.CommonName)
}

keyPKCS8, err := key.PKCS8()
if err != nil {
return nil, errors.Wrapf(err, "encoding private key as PKCS8 for %s",
r.CommonName)
}

keyPem := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: keyPKCS8,
})
secret.Data[KeyPrivateKey] = string(keyPem)

lease, err := LeaseFromSecret(r, secret)
if err != nil {
return nil, errors.Wrapf(err,
"making lease from generated certificate for %s", r.CommonName)
}
return lease, nil
})
}
Loading

0 comments on commit d417aae

Please sign in to comment.