Skip to content

Commit

Permalink
switch to vcert certificate.Request
Browse files Browse the repository at this point in the history
  • Loading branch information
arykalin committed Feb 28, 2019
1 parent 0a6afb0 commit 5ea70c0
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 162 deletions.
69 changes: 43 additions & 26 deletions plugin/pki/path_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,21 @@ Example:
Description: `Set it to true to store certificates by unique serial number in certs/ path`,
},

"service_generated_cert": {
Type: framework.TypeBool,
Description: `Use service generated CSR for Venafi Platfrom (ignored if Saas endpoint used)`,
Default: false,
},
"store_pkey": {
Type: framework.TypeBool,
Description: `Set it to true to store certificates privates key in certificate fields`,
},
"chain_option": {
Type: framework.TypeString,
Description: `Specify ordering certificates in chain. Root can be "first" or
"last"`,
Default: "last",
},
"key_type": {
Type: framework.TypeString,
Default: "rsa",
Expand Down Expand Up @@ -242,23 +253,25 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data
name := data.Get("name").(string)

entry := &roleEntry{
TPPURL: data.Get("tpp_url").(string),
CloudURL: data.Get("cloud_url").(string),
Zone: data.Get("zone").(string),
TPPPassword: data.Get("tpp_password").(string),
Apikey: data.Get("apikey").(string),
TPPUser: data.Get("tpp_user").(string),
TrustBundleFile: data.Get("trust_bundle_file").(string),
Fakemode: data.Get("fakemode").(bool),
StoreByCN: data.Get("store_by_cn").(bool),
StoreBySerial: data.Get("store_by_serial").(bool),
StorePrivateKey: data.Get("store_pkey").(bool),
KeyType: data.Get("key_type").(string),
KeyBits: data.Get("key_bits").(int),
KeyCurve: data.Get("key_curve").(string),
MaxTTL: time.Duration(data.Get("max_ttl").(int)) * time.Second,
TTL: time.Duration(data.Get("ttl").(int)) * time.Second,
GenerateLease: data.Get("generate_lease").(bool),
TPPURL: data.Get("tpp_url").(string),
CloudURL: data.Get("cloud_url").(string),
Zone: data.Get("zone").(string),
TPPPassword: data.Get("tpp_password").(string),
Apikey: data.Get("apikey").(string),
TPPUser: data.Get("tpp_user").(string),
TrustBundleFile: data.Get("trust_bundle_file").(string),
Fakemode: data.Get("fakemode").(bool),
ChainOption: data.Get("chain_option").(string),
StoreByCN: data.Get("store_by_cn").(bool),
StoreBySerial: data.Get("store_by_serial").(bool),
ServiceGenerated: data.Get("service_generated_cert").(bool),
StorePrivateKey: data.Get("store_pkey").(bool),
KeyType: data.Get("key_type").(string),
KeyBits: data.Get("key_bits").(int),
KeyCurve: data.Get("key_curve").(string),
MaxTTL: time.Duration(data.Get("max_ttl").(int)) * time.Second,
TTL: time.Duration(data.Get("ttl").(int)) * time.Second,
GenerateLease: data.Get("generate_lease").(bool),
}
if !entry.Fakemode && entry.Apikey == "" && (entry.TPPURL == "" || entry.TPPUser == "" || entry.TPPPassword == "") {
return logical.ErrorResponse("Invalid mode. fakemode or apikey or tpp credentials required"), nil
Expand Down Expand Up @@ -292,8 +305,10 @@ type roleEntry struct {
TPPUser string `json:"tpp_user"`
TrustBundleFile string `json:"trust_bundle_file"`
Fakemode bool `json:"fakemode"`
ChainOption string `json:"chain_option"`
StoreByCN bool `json:"store_by_cn"`
StoreBySerial bool `json:"store_by_serial"`
ServiceGenerated bool `json:"service_generated_cert"`
StorePrivateKey bool `json:"store_pkey"`
KeyType string `json:"key_type"`
KeyBits int `json:"key_bits"`
Expand All @@ -316,15 +331,17 @@ func (r *roleEntry) ToResponseData() map[string]interface{} {
//We shouldn't show credentials
//"tpp_password": r.TPPPassword,
//"apikey": r.Apikey,
"tpp_user": r.TPPUser,
"trust_bundle_file": r.TrustBundleFile,
"fakemode": r.Fakemode,
"store_by_cn": r.StoreByCN,
"store_by_serial": r.StoreBySerial,
"store_pkey": r.StorePrivateKey,
"ttl": int64(r.TTL.Seconds()),
"max_ttl": int64(r.MaxTTL.Seconds()),
"generate_lease": r.GenerateLease,
"tpp_user": r.TPPUser,
"trust_bundle_file": r.TrustBundleFile,
"fakemode": r.Fakemode,
"store_by_cn": r.StoreByCN,
"store_by_serial": r.StoreBySerial,
"service_generated_cert": r.ServiceGenerated,
"store_pkey": r.StorePrivateKey,
"ttl": int64(r.TTL.Seconds()),
"max_ttl": int64(r.MaxTTL.Seconds()),
"generate_lease": r.GenerateLease,
"chain_option": r.ChainOption,
}
return responseData
}
Expand Down
206 changes: 70 additions & 136 deletions plugin/pki/path_venafi_cert_enroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package pki

import (
"context"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
vcertificate "github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
Expand Down Expand Up @@ -75,67 +75,95 @@ func (b *backend) pathVenafiCertObtain(ctx context.Context, req *logical.Request
var pk privateKey

pk.keyType = role.KeyType
pk.keyBits = role.KeyBits
pk.keyCurve = role.KeyCurve
log.Printf("Signing private key with parameteres %v", pk)

certReq, pkey, err := createVenafiCSR(commonName, altNames, pk)
certReq := &certificate.Request{
Subject: pkix.Name{
CommonName: commonName,
},
DNSNames: altNames,
//TODO: add email, ip address parsing or split this options in certificate request
//EmailAddresses: []string{"[email protected]", "[email protected]"},
//IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv4(127, 0, 0, 2)},
CsrOrigin: certificate.LocalGeneratedCSR,
//TODO: add key password support
}

if cl.GetType() == endpoint.ConnectorTypeTPP && role.ServiceGenerated {
log.Println("Using service generated certificate for Venafi Platform")
certReq.CsrOrigin = certificate.ServiceGeneratedCSR
}

if role.KeyType == "rsa" {
certReq.KeyLength = role.KeyBits
} else if role.KeyType == "ec" {
certReq.KeyType = certificate.KeyTypeECDSA
switch {
case role.KeyCurve == "P224":
certReq.KeyCurve = certificate.EllipticCurveP224
case role.KeyCurve == "P256":
certReq.KeyCurve = certificate.EllipticCurveP256
case role.KeyCurve == "P384":
certReq.KeyCurve = certificate.EllipticCurveP384
case role.KeyCurve == "P521":
certReq.KeyCurve = certificate.EllipticCurveP521
default:
return logical.ErrorResponse(fmt.Sprintf("can't use key curve %s", role.KeyCurve)), nil
}

} else {
return logical.ErrorResponse(fmt.Sprintf("can't determine key algorithm for %s", role.KeyType)), nil
}


if role.ChainOption == "first" {
certReq.ChainOption = certificate.ChainOptionRootFirst
} else if role.ChainOption == "last" {
certReq.ChainOption = certificate.ChainOptionRootLast
} else {
return logical.ErrorResponse(fmt.Sprintf("Invalid chain option %s", role.ChainOption)), nil
}

log.Println("Making certificate request")
err = cl.GenerateRequest(nil, certReq)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}

log.Printf("Running enroll request")
id, err := cl.RequestCertificate(certReq, "")

requestID, err := cl.RequestCertificate(certReq, "")
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}

certReq.PickupID = id

var cert *vcertificate.PEMCollection
for {
cert, err = cl.RetrieveCertificate(certReq)
if err != nil {
_, pending := err.(endpoint.ErrCertificatePending)
_, timeout := err.(endpoint.ErrRetrieveCertificateTimeout)

if pending || timeout {
log.Printf("Certificate %s Issue pending with id %s", commonName, id)
time.Sleep(5 * time.Second)
continue
} else {
return logical.ErrorResponse(err.Error()), nil
}
}
log.Printf("Certificate is %s", *cert)
log.Printf("successfully got certificate: cn=%q altNames=%+v", commonName, altNames)
break
pickupReq := &certificate.Request{
PickupID: requestID,
//TODO: make timeout configurable
Timeout: 180 * time.Second,
}
pcc, err := cl.RetrieveCertificate(pickupReq)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}

certificate := strings.Join([]string{cert.Certificate}, "\n")
cs := append([]string{cert.Certificate}, cert.Chain...)
chain := strings.Join(cs, "\n")
log.Println("certificate: ", chain)

//Parsing certificate and getting it's serial number
pemBlock, _ := pem.Decode([]byte(certificate))
pemBlock, _ := pem.Decode([]byte(pcc.Certificate))
parsedCertificate, err := x509.ParseCertificate(pemBlock.Bytes)
serialNumber := getHexFormatted(parsedCertificate.SerialNumber.Bytes(), ":")

encoded_key := pem.EncodeToMemory(pkey)
log.Println("Writing chain:", chain, "And key: ", string(encoded_key))

var entry *logical.StorageEntry

chain := strings.Join(append([]string{pcc.Certificate}, pcc.Chain...), "\n")
pcc.AddPrivateKey(certReq.PrivateKey, []byte(""))
if role.StorePrivateKey {
entry, err = logical.StorageEntryJSON("", VenafiCert{
Certificate: certificate,
Certificate: pcc.Certificate,
CertificateChain: chain,
PrivateKey: string(encoded_key),
PrivateKey: pcc.PrivateKey,
SerialNumber: serialNumber,
})
} else {
entry, err = logical.StorageEntryJSON("", VenafiCert{
Certificate: certificate,
Certificate: pcc.Certificate,
CertificateChain: chain,
SerialNumber: serialNumber,
})
Expand Down Expand Up @@ -169,8 +197,8 @@ func (b *backend) pathVenafiCertObtain(ctx context.Context, req *logical.Request
"common_name": commonName,
"serial_number": serialNumber,
"certificate_chain": chain,
"certificate": certificate,
"private_key": string(encoded_key),
"certificate": pcc.Certificate,
"private_key": pcc.PrivateKey,
}

var logResp *logical.Response
Expand Down Expand Up @@ -202,100 +230,6 @@ type privateKey struct {
keyType string
}

func createVenafiCSR(commonName string, altNames []string, pk privateKey) (*vcertificate.Request, *pem.Block, error) {
var err error
const defaultKeySize = 2048
req := &vcertificate.Request{}

if len(commonName) == 0 && len(altNames) == 0 {
return req, nil, fmt.Errorf("no domains specified on certificate")
}
if len(commonName) == 0 && len(altNames) > 0 {
commonName = altNames[0]
}

//Obtain a certificate from the Venafi server
log.Printf("Using CN %s and SAN %s", commonName, altNames)
req.Subject.CommonName = commonName
//Adding alt names if exists
dnsnum := len(altNames)
if dnsnum > 0 {
req.DNSNames = make([]string, 0, dnsnum)
for i := 0; i < dnsnum; i++ {
val := altNames[i]
log.Printf("Adding SAN %s.", val)
req.DNSNames = append(req.DNSNames, val)
}
}
//Appending common name to the DNS names if it is not there
if !sliceContains(req.DNSNames, commonName) {
log.Printf("Adding CN %s to SAN because it wasn't included.", commonName)
req.DNSNames = append(req.DNSNames, commonName)
}

log.Printf("Requested SAN: %s", req.DNSNames)

if pk.keyType == "rsa" {
req.KeyLength = pk.keyBits
} else if pk.keyType == "ec" {
req.KeyType = vcertificate.KeyTypeECDSA
switch {
case pk.keyCurve == "P224":
req.KeyCurve = vcertificate.EllipticCurveP224
case pk.keyCurve == "P256":
req.KeyCurve = vcertificate.EllipticCurveP256
case pk.keyCurve == "P384":
req.KeyCurve = vcertificate.EllipticCurveP384
case pk.keyCurve == "P521":
req.KeyCurve = vcertificate.EllipticCurveP521
default:

}

} else {
return req, nil, fmt.Errorf("can't determine key algorithm %s", pk.keyType)
}

switch req.KeyType {
case vcertificate.KeyTypeECDSA:
req.PrivateKey, err = vcertificate.GenerateECDSAPrivateKey(req.KeyCurve)
case vcertificate.KeyTypeRSA:
req.PrivateKey, err = vcertificate.GenerateRSAPrivateKey(req.KeyLength)
default:
log.Printf("Unable to generate certificate request, key type %s is not supported", req.KeyType.String())
return nil, nil, err
}

key, err := getPrivateKeyPEMBock(req.PrivateKey)
if err != nil {
return nil, nil, err
}

//Setting up CSR
certificateRequest := x509.CertificateRequest{}
certificateRequest.Subject = req.Subject
certificateRequest.DNSNames = req.DNSNames
certificateRequest.EmailAddresses = req.EmailAddresses
certificateRequest.IPAddresses = req.IPAddresses
certificateRequest.Attributes = req.Attributes

/* TODO:
zoneConfig, err = cs.Conn.ReadZoneConfiguration(cf.Zone)
zoneConfig.UpdateCertificateRequest(req)
...should happen somewhere here before CSR is signed */

csr, err := x509.CreateCertificateRequest(rand.Reader, &certificateRequest, req.PrivateKey)
if err != nil {
return nil, nil, err
}

req.CSR = csr

req.CSR = pem.EncodeToMemory(vcertificate.GetCertificateRequestPEMBlock(req.CSR))

return req, key, nil
}

type VenafiCert struct {
Certificate string `json:"certificate"`
CertificateChain string `json:"certificate_chain"`
Expand Down

0 comments on commit 5ea70c0

Please sign in to comment.