Bump vcert to version 'master'

Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
James Munnelly
2019-06-06 14:56:18 +01:00
parent 1205dc5ef8
commit dc60a2d73e
19 changed files with 1307 additions and 1061 deletions
+1 -2
View File
@@ -10,7 +10,7 @@ require (
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/SAP/go-hdb v0.14.1 // indirect
github.com/SermoDigital/jose v0.9.1 // indirect
github.com/Venafi/vcert v0.0.0-20181029235941-5068538d4d65
github.com/Venafi/vcert v0.0.0-20190530133915-e207710a0ab9
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 // indirect
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
github.com/armon/go-radix v1.0.0 // indirect
@@ -87,7 +87,6 @@ require (
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0 // indirect
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/spf13/cobra v0.0.0-20170905172051-b78744579491
github.com/spf13/pflag v1.0.1
github.com/stretchr/testify v1.3.0
+7 -2
View File
@@ -32,8 +32,8 @@ github.com/SermoDigital/jose v0.9.1 h1:atYaHPD3lPICcbK1owly3aPm0iaJGSGPi0WD4vLzn
github.com/SermoDigital/jose v0.9.1/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/Venafi/vcert v0.0.0-20181029235941-5068538d4d65 h1:An1XpraQx4IkI2i0kcXFS+VjTSK/lRb1ZioZRmTdDMA=
github.com/Venafi/vcert v0.0.0-20181029235941-5068538d4d65/go.mod h1:3dpfrCI+31cDZosD+1UX8GFziVFORaegByXtzT1dwNo=
github.com/Venafi/vcert v0.0.0-20190530133915-e207710a0ab9 h1:dt3QeZOpoi4ee72KwblGKmX2v6WbVS37yjCeZq6SV9w=
github.com/Venafi/vcert v0.0.0-20190530133915-e207710a0ab9/go.mod h1:3sXw16DKVded/kLVDma2veqEUQC7O37h98ims7cIvN4=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -226,6 +226,7 @@ github.com/hashicorp/vault v0.9.6 h1:AalVi4vunOMSXX7eD8j8WKxkWr2AmG6yvz9o3ITSaMc
github.com/hashicorp/vault v0.9.6/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
@@ -392,6 +393,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -436,6 +438,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190425045458-9f0b1ff7b46a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -491,6 +494,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
@@ -543,3 +547,4 @@ sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkW
sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
+14 -6
View File
@@ -25,32 +25,40 @@ import (
"github.com/Venafi/vcert/pkg/venafi/tpp"
)
// NewClient returns a connector for either Trust Protection Platform (TPP) or Venafi Cloud based on provided configuration.
// Config should have Credentials compatible with the selected ConnectorType.
// Returned connector is a concurrency-safe interface to TPP or Venafi Cloud that can be reused without restriction.
// Connector can also be of type "fake" for local tests, which doesn`t connect to any backend and all certificates enroll locally.
func NewClient(cfg *Config) (endpoint.Connector, error) {
var err error
var connectionTrustBundle *x509.CertPool
if cfg.ConnectionTrust != "" {
fmt.Println("You specified a trust bundle.")
connectionTrustBundle = x509.NewCertPool()
if !connectionTrustBundle.AppendCertsFromPEM([]byte(cfg.ConnectionTrust)) {
return nil, fmt.Errorf("failed to parse PEM trust bundle")
return nil, fmt.Errorf("Failed to parse PEM trust bundle")
}
}
var connector endpoint.Connector
switch cfg.ConnectorType {
case endpoint.ConnectorTypeCloud:
connector = cloud.NewConnector(cfg.LogVerbose, connectionTrustBundle)
connector, err = cloud.NewConnector(cfg.BaseUrl, cfg.Zone, cfg.LogVerbose, connectionTrustBundle)
if err != nil {
return nil, err
}
case endpoint.ConnectorTypeTPP:
connector = tpp.NewConnector(cfg.LogVerbose, connectionTrustBundle)
connector, err = tpp.NewConnector(cfg.BaseUrl, cfg.Zone, cfg.LogVerbose, connectionTrustBundle)
if err != nil {
return nil, err
}
case endpoint.ConnectorTypeFake:
connector = fake.NewConnector(cfg.LogVerbose, connectionTrustBundle)
default:
return nil, fmt.Errorf("ConnectorType is not defined")
}
if cfg.BaseUrl != "" {
connector.SetBaseURL(cfg.BaseUrl)
}
connector.SetZone(cfg.Zone)
err = connector.Authenticate(cfg.Credentials)
+38 -32
View File
@@ -26,51 +26,57 @@ import (
"path/filepath"
)
// Config is a basic structure for high level initiating connector to Trust Platform (TPP)/Venafi Cloud
type Config struct {
ConnectorType endpoint.ConnectorType
BaseUrl string
Zone string
Credentials *endpoint.Authentication
// ConnectorType specify what do you want to use. May be "Cloud", "TPP" or "Fake" for development.
ConnectorType endpoint.ConnectorType
// BaseUrl should be specified for Venafi Platform. Optional for Cloud implementations that do not use https://venafi.cloud/.
BaseUrl string
// Zone is name of a policy zone in Venafi Platform or Cloud. For TPP, if necessary, escape backslash symbols. For example, "test\\zone" or `test\zone`.
Zone string
// Credentials should contain either User and Password for TPP connections or an APIKey for Cloud.
Credentials *endpoint.Authentication
// ConnectionTrust may contain a trusted CA or certificate of server if you use self-signed certificate.
ConnectionTrust string // *x509.CertPool
LogVerbose bool
ConfigFile string
ConfigSection string
}
func (cfg *Config) LoadFromFile() error {
if cfg.ConfigSection == "" {
cfg.ConfigSection = ini.DEFAULT_SECTION
}
log.Printf("Loading configuration from %s section %s", cfg.ConfigFile, cfg.ConfigSection)
// LoadFromFile is deprecated. In the future will be rewrited.
func LoadConfigFromFile(path, section string) (cfg Config, err error) {
fname, err := expand(cfg.ConfigFile)
if section == "" {
section = ini.DEFAULT_SECTION
}
log.Printf("Loading configuration from %s section %s", path, section)
fname, err := expand(path)
if err != nil {
return fmt.Errorf("failed to load config: %s", err)
return cfg, fmt.Errorf("failed to load config: %s", err)
}
iniFile, err := ini.Load(fname)
if err != nil {
return fmt.Errorf("failed to load config: %s", err)
return cfg, fmt.Errorf("failed to load config: %s", err)
}
err = validateFile(iniFile)
if err != nil {
return fmt.Errorf("failed to load config: %s", err)
return cfg, fmt.Errorf("failed to load config: %s", err)
}
ok := func() bool {
for _, section := range iniFile.Sections() {
if section.Name() == cfg.ConfigSection {
for _, s := range iniFile.Sections() {
if s.Name() == section {
return true
}
}
return false
}()
if !ok {
return fmt.Errorf("section %s has not been found in %s", cfg.ConfigSection, cfg.ConfigFile)
return cfg, fmt.Errorf("section %s has not been found in %s", section, path)
}
var m Dict = iniFile.Section(cfg.ConfigSection).KeysHash()
var m dict = iniFile.Section(section).KeysHash()
var connectorType endpoint.ConnectorType
var baseUrl string
@@ -98,17 +104,17 @@ func (cfg *Config) LoadFromFile() error {
} else if m.has("test_mode") && m["test_mode"] == "true" {
connectorType = endpoint.ConnectorTypeFake
} else {
return fmt.Errorf("failed to load config: connector type cannot be defined")
return cfg, fmt.Errorf("failed to load config: connector type cannot be defined")
}
if m.has("trust_bundle") {
fname, err := expand(m["trust_bundle"])
if err != nil {
return fmt.Errorf("failed to load trust-bundle: %s", err)
return cfg, fmt.Errorf("failed to load trust-bundle: %s", err)
}
data, err := ioutil.ReadFile(fname)
if err != nil {
return fmt.Errorf("failed to load trust-bundle: %s", err)
return cfg, fmt.Errorf("failed to load trust-bundle: %s", err)
}
cfg.ConnectionTrust = string(data)
}
@@ -117,7 +123,7 @@ func (cfg *Config) LoadFromFile() error {
cfg.Credentials = auth
cfg.BaseUrl = baseUrl
return nil
return
}
func expand(path string) (string, error) {
@@ -131,18 +137,18 @@ func expand(path string) (string, error) {
return filepath.Join(usr.HomeDir, path[1:]), nil
}
type Dict map[string]string
type dict map[string]string
func (d Dict) has(key string) bool {
func (d dict) has(key string) bool {
if _, ok := d[key]; ok {
return true
}
return false
}
type Set map[string]bool
type set map[string]bool
func (d Set) has(key string) bool {
func (d set) has(key string) bool {
if _, ok := d[key]; ok {
return true
}
@@ -150,14 +156,14 @@ func (d Set) has(key string) bool {
}
func validateSection(s *ini.Section) error {
var TPPValidKeys Set = map[string]bool{
var TPPValidKeys set = map[string]bool{
"tpp_url": true,
"tpp_user": true,
"tpp_password": true,
"tpp_zone": true,
"trust_bundle": true,
}
var CloudValidKeys Set = map[string]bool{
var CloudValidKeys set = map[string]bool{
"trust_bundle": true,
"cloud_url": true,
"cloud_apikey": true,
@@ -165,11 +171,11 @@ func validateSection(s *ini.Section) error {
}
log.Printf("Validating configuration section %s", s.Name())
var m Dict = s.KeysHash()
var m dict = s.KeysHash()
if m.has("tpp_url") {
// looks like TPP config section
for k, _ := range m {
for k := range m {
if !TPPValidKeys.has(k) {
return fmt.Errorf("illegal key '%s' in TPP section %s", k, s.Name())
}
@@ -182,7 +188,7 @@ func validateSection(s *ini.Section) error {
}
} else if m.has("cloud_apikey") {
// looks like Cloud config section
for k, _ := range m {
for k := range m {
if !CloudValidKeys.has(k) {
return fmt.Errorf("illegal key '%s' in Cloud section %s", k, s.Name())
}
+206 -54
View File
@@ -17,6 +17,7 @@
package certificate
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -30,7 +31,7 @@ import (
"time"
)
//EllipticCurve represents the types of supported elliptic curves
// EllipticCurve represents the types of supported elliptic curves
type EllipticCurve int
func (ec *EllipticCurve) String() string {
@@ -48,7 +49,7 @@ func (ec *EllipticCurve) String() string {
}
}
//Set the elliptic cuve value via a string
// Set EllipticCurve value via a string
func (ec *EllipticCurve) Set(value string) error {
switch strings.ToLower(value) {
case "p521":
@@ -60,24 +61,34 @@ func (ec *EllipticCurve) Set(value string) error {
case "p224":
*ec = EllipticCurveP224
default:
*ec = EllipticCurveP521
*ec = EllipticCurveDefault
}
return nil
}
const (
//EllipticCurveP521 represents the P521 curve
// EllipticCurveP521 represents the P521 curve
EllipticCurveP521 EllipticCurve = iota
//EllipticCurveP224 represents the P224 curve
// EllipticCurveP224 represents the P224 curve
EllipticCurveP224
//EllipticCurveP256 represents the P256 curve
// EllipticCurveP256 represents the P256 curve
EllipticCurveP256
//EllipticCurveP384 represents the P384 curve
// EllipticCurveP384 represents the P384 curve
EllipticCurveP384
EllipticCurveDefault = EllipticCurveP521
defaultRSAlength int = 2048
)
//KeyType represents the types of supported keys
func AllSupportedCurves() []EllipticCurve {
return []EllipticCurve{EllipticCurveP521, EllipticCurveP224, EllipticCurveP256, EllipticCurveP384}
}
func AllSupportedKeySizes() []int {
return []int{512, 1024, 2048, 4096, 8192}
}
// KeyType represents the types of supported keys
type KeyType int
func (kt *KeyType) String() string {
@@ -91,59 +102,75 @@ func (kt *KeyType) String() string {
}
}
//Set the key type via a string
func (kt *KeyType) X509Type() x509.PublicKeyAlgorithm {
switch *kt {
case KeyTypeRSA:
return x509.RSA
case KeyTypeECDSA:
return x509.ECDSA
}
return x509.UnknownPublicKeyAlgorithm
}
// Set the key type via a string
func (kt *KeyType) Set(value string) error {
switch strings.ToLower(value) {
case "rsa":
*kt = KeyTypeRSA
case "ecdsa":
*kt = KeyTypeECDSA
default:
return nil
case "ecdsa", "ec", "ecc":
*kt = KeyTypeECDSA
return nil
}
return nil
return fmt.Errorf("unknow key type: %s", value) //todo: check all calls
}
const (
//KeyTypeRSA represents a key type of RSA
// KeyTypeRSA represents a key type of RSA
KeyTypeRSA KeyType = iota
//KeyTypeECDSA represents a key type of ECDSA
// KeyTypeECDSA represents a key type of ECDSA
KeyTypeECDSA
)
type CSrOriginOption int
const (
// LocalGeneratedCSR - this vcert library generates CSR internally based on Request data
LocalGeneratedCSR CSrOriginOption = iota // local generation is default.
// ServiceGeneratedCSR - server generate CSR internally based on zone configuration and data from Request
ServiceGeneratedCSR
// UserProvidedCSR - client provides CSR from external resource and vcert library just check and send this CSR to server
UserProvidedCSR
)
//Request contains data needed to generate a certificate request
// Request contains data needed to generate a certificate request
// CSR is a PEM-encoded Certificate Signing Request
type Request struct {
CADN string
Subject pkix.Name
DNSNames []string
EmailAddresses []string
IPAddresses []net.IP
Attributes []pkix.AttributeTypeAndValueSET
SignatureAlgorithm x509.SignatureAlgorithm
PublicKeyAlgorithm x509.PublicKeyAlgorithm
FriendlyName string
KeyType KeyType
KeyLength int
KeyCurve EllipticCurve
CSR []byte
PrivateKey interface{}
CsrOrigin CSrOriginOption
PickupID string
ChainOption ChainOption
KeyPassword string
FetchPrivateKey bool
Thumbprint string /* this one is here because *Request is used in RetrieveCertificate(),
it should be refactored so that RetrieveCertificate() uses
some abstract search object, instead of *Request{PickupID} */
Timeout time.Duration
//CSR []byte // should be a PEM-encoded CSR
csr []byte // should be a PEM-encoded CSR
PrivateKey crypto.Signer
CsrOrigin CSrOriginOption
PickupID string
//Cloud Certificate ID
CertID string
ChainOption ChainOption
KeyPassword string
FetchPrivateKey bool
/* Thumbprint is here because *Request is used in RetrieveCertificate().
Code should be refactored so that RetrieveCertificate() uses some abstract search object, instead of *Request{PickupID} */
Thumbprint string
Timeout time.Duration
}
type RevocationRequest struct {
@@ -172,13 +199,52 @@ type ImportRequest struct {
type ImportResponse struct {
CertificateDN string `json:",omitempty"`
CertId string `json:",omitempty"`
CertificateVaultId int `json:",omitempty"`
Guid string `json:",omitempty"`
PrivateKeyVaultId int `json:",omitempty"`
}
//GenerateRequest generates a certificate request
func GenerateRequest(request *Request, privateKey interface{}) error {
// SetCSR sets CSR from PEM or DER format
func (request *Request) SetCSR(csr []byte) error {
pemBlock, _ := pem.Decode(csr)
if pemBlock != nil {
if pemBlock.Type == "CERTIFICATE REQUEST" {
request.csr = csr
return nil
}
}
//Determine CSR type and use appropriate function
parsedCSR, err := x509.ParseCertificateRequest(csr)
if err != nil {
return err
}
if parsedCSR != nil {
request.csr = pem.EncodeToMemory(GetCertificateRequestPEMBlock(csr))
return nil
}
return fmt.Errorf("Can't determine CSR type for %s", csr)
}
// GetCSR returns CSR in PEM format
func (request Request) GetCSR() []byte {
return request.csr
}
// GenerateRequest generates a certificate request
// Please use method Request.GenerateCSR()
// TODO: Remove usage from all libraries, deprecated
func GenerateRequest(request *Request, privateKey crypto.Signer) error {
pk := request.PrivateKey
request.PrivateKey = privateKey
err := request.GenerateCSR()
request.PrivateKey = pk
return err
}
// GenerateCSR creates CSR for sending to server based on data from Request fields. It rewrites CSR field if it`s already filled.
func (request *Request) GenerateCSR() error {
certificateRequest := x509.CertificateRequest{}
certificateRequest.Subject = request.Subject
certificateRequest.DNSNames = request.DNSNames
@@ -186,32 +252,118 @@ func GenerateRequest(request *Request, privateKey interface{}) error {
certificateRequest.IPAddresses = request.IPAddresses
certificateRequest.Attributes = request.Attributes
csr, err := x509.CreateCertificateRequest(rand.Reader, &certificateRequest, privateKey)
csr, err := x509.CreateCertificateRequest(rand.Reader, &certificateRequest, request.PrivateKey)
if err != nil {
csr = nil
}
request.CSR = csr
err = request.SetCSR(csr)
//request.CSR = pem.EncodeToMemory(GetCertificateRequestPEMBlock(csr))
return err
}
func publicKey(priv interface{}) interface{} {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &k.PublicKey
case *ecdsa.PrivateKey:
return &k.PublicKey
default:
// GeneratePrivateKey creates private key (if it doesn`t already exist) based on request.KeyType, request.KeyLength and request.KeyCurve fileds
func (request *Request) GeneratePrivateKey() error {
if request.PrivateKey != nil {
return nil
}
var err error
switch request.KeyType {
case KeyTypeECDSA:
request.PrivateKey, err = GenerateECDSAPrivateKey(request.KeyCurve)
case KeyTypeRSA:
if request.KeyLength == 0 {
request.KeyLength = defaultRSAlength
}
request.PrivateKey, err = GenerateRSAPrivateKey(request.KeyLength)
default:
return fmt.Errorf("Unable to generate certificate request, key type %s is not supported", request.KeyType.String())
}
return err
}
func PublicKey(priv interface{}) interface{} {
// CheckCertificate validate that certificate returned by server matches data in request object. It can be used for control server.
func (request *Request) CheckCertificate(certPEM string) error {
pemBlock, _ := pem.Decode([]byte(certPEM))
if pemBlock == nil {
return fmt.Errorf("invalid pem format certificate %s", certPEM)
}
if pemBlock.Type != "CERTIFICATE" {
return fmt.Errorf("invalid pem type %s (expect CERTIFICATE)", pemBlock.Type)
}
cert, err := x509.ParseCertificate(pemBlock.Bytes)
if err != nil {
return err
}
if request.PrivateKey != nil {
if request.KeyType.X509Type() != cert.PublicKeyAlgorithm {
return fmt.Errorf("unmatched key type: %s, %s", request.KeyType.X509Type(), cert.PublicKeyAlgorithm)
}
switch cert.PublicKeyAlgorithm {
case x509.RSA:
certPubKey := cert.PublicKey.(*rsa.PublicKey)
reqPubkey, ok := request.PrivateKey.Public().(*rsa.PublicKey)
if !ok {
return fmt.Errorf("request KeyType not matched with real PrivateKey type")
}
if certPubKey.N.Cmp(reqPubkey.N) != 0 {
return fmt.Errorf("unmatched key modules")
}
case x509.ECDSA:
certPubkey := cert.PublicKey.(*ecdsa.PublicKey)
reqPubkey, ok := request.PrivateKey.Public().(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("request KeyType not matched with real PrivateKey type")
}
if certPubkey.X.Cmp(reqPubkey.X) != 0 {
return fmt.Errorf("unmatched X for eliptic keys")
}
default:
return fmt.Errorf("unknown key algorythm %d", cert.PublicKeyAlgorithm)
}
} else if len(request.csr) != 0 {
pemBlock, _ := pem.Decode(request.csr)
if pemBlock == nil {
return fmt.Errorf("bad csr: %s", string(request.csr))
}
csr, err := x509.ParseCertificateRequest(pemBlock.Bytes)
if err != nil {
return err
}
if cert.PublicKeyAlgorithm != csr.PublicKeyAlgorithm {
return fmt.Errorf("unmatched key type: %s, %s", cert.PublicKeyAlgorithm, csr.PublicKeyAlgorithm)
}
switch csr.PublicKeyAlgorithm {
case x509.RSA:
certPubKey := cert.PublicKey.(*rsa.PublicKey)
reqPubKey := csr.PublicKey.(*rsa.PublicKey)
if certPubKey.N.Cmp(reqPubKey.N) != 0 {
return fmt.Errorf("unmatched key modules")
}
case x509.ECDSA:
certPubKey := cert.PublicKey.(*ecdsa.PublicKey)
reqPubKey := csr.PublicKey.(*ecdsa.PublicKey)
if certPubKey.X.Cmp(reqPubKey.X) != 0 {
return fmt.Errorf("unmatched X for eliptic keys")
}
}
}
return nil
}
func publicKey(priv crypto.Signer) crypto.PublicKey {
if priv != nil {
return priv.Public()
}
return nil
}
func PublicKey(priv crypto.Signer) crypto.PublicKey {
return publicKey(priv)
}
//GetPrivateKeyPEMBock gets the private key as a PEM data block
func GetPrivateKeyPEMBock(key interface{}) (*pem.Block, error) {
// GetPrivateKeyPEMBock gets the private key as a PEM data block
func GetPrivateKeyPEMBock(key crypto.Signer) (*pem.Block, error) { // TODO: Change to crypto.Signer type
switch k := key.(type) {
case *rsa.PrivateKey:
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil
@@ -226,8 +378,8 @@ func GetPrivateKeyPEMBock(key interface{}) (*pem.Block, error) {
}
}
//GetEncryptedPrivateKeyPEMBock gets the private key as an encrypted PEM data block
func GetEncryptedPrivateKeyPEMBock(key interface{}, password []byte) (*pem.Block, error) {
// GetEncryptedPrivateKeyPEMBock gets the private key as an encrypted PEM data block
func GetEncryptedPrivateKeyPEMBock(key crypto.Signer, password []byte) (*pem.Block, error) { // TODO: Change to crypto.Signer type
switch k := key.(type) {
case *rsa.PrivateKey:
return x509.EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(k), password, x509.PEMCipherAES256)
@@ -242,17 +394,17 @@ func GetEncryptedPrivateKeyPEMBock(key interface{}, password []byte) (*pem.Block
}
}
//GetCertificatePEMBlock gets the certificate as a PEM data block
// GetCertificatePEMBlock gets the certificate as a PEM data block
func GetCertificatePEMBlock(cert []byte) *pem.Block {
return &pem.Block{Type: "CERTIFICATE", Bytes: cert}
}
//GetCertificateRequestPEMBlock gets the certificate request as a PEM data block
// GetCertificateRequestPEMBlock gets the certificate request as a PEM data block
func GetCertificateRequestPEMBlock(request []byte) *pem.Block {
return &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: request}
}
//GenerateECDSAPrivateKey generates a new ecdsa private key using the curve specified
// GenerateECDSAPrivateKey generates a new ecdsa private key using the curve specified
func GenerateECDSAPrivateKey(curve EllipticCurve) (*ecdsa.PrivateKey, error) {
var priv *ecdsa.PrivateKey
var c elliptic.Curve
@@ -277,7 +429,7 @@ func GenerateECDSAPrivateKey(curve EllipticCurve) (*ecdsa.PrivateKey, error) {
return priv, nil
}
//GenerateRSAPrivateKey generates a new rsa private key using the size specified
// GenerateRSAPrivateKey generates a new rsa private key using the size specified
func GenerateRSAPrivateKey(size int) (*rsa.PrivateKey, error) {
priv, err := rsa.GenerateKey(rand.Reader, size)
if err != nil {
@@ -287,16 +439,16 @@ func GenerateRSAPrivateKey(size int) (*rsa.PrivateKey, error) {
return priv, nil
}
// NewRequest duplicates new Request object based on issued certificate
func NewRequest(cert *x509.Certificate) *Request {
req := &Request{}
// 1st fill with *cert content
// First populate with *cert content
req.Subject = cert.Subject
req.DNSNames = cert.DNSNames
req.EmailAddresses = cert.EmailAddresses
req.IPAddresses = cert.IPAddresses
req.SignatureAlgorithm = cert.SignatureAlgorithm
req.PublicKeyAlgorithm = cert.PublicKeyAlgorithm
switch pub := cert.PublicKey.(type) {
case *rsa.PublicKey:
req.KeyType = KeyTypeRSA
@@ -304,8 +456,8 @@ func NewRequest(cert *x509.Certificate) *Request {
case *ecdsa.PublicKey:
req.KeyType = KeyTypeECDSA
req.KeyLength = pub.Curve.Params().BitSize
// TODO: req.KeyCurve = pub.Curve.Params().Name...
default: // case *dsa.PublicKey:
// TODO: req.KeyCurve = pub.Curve.Params().Name ...
default: // case *dsa.PublicKey
// vcert only works with RSA & ECDSA
}
return req
+13 -6
View File
@@ -17,6 +17,7 @@
package certificate
import (
"crypto"
"crypto/x509"
"encoding/pem"
"fmt"
@@ -55,7 +56,7 @@ type PEMCollection struct {
}
//NewPEMCollection creates a PEMCollection based on the data being passed in
func NewPEMCollection(certificate *x509.Certificate, privateKey interface{}, privateKeyPassword []byte) (*PEMCollection, error) {
func NewPEMCollection(certificate *x509.Certificate, privateKey crypto.Signer, privateKeyPassword []byte) (*PEMCollection, error) { //todo: change to crypto.Signer type
collection := PEMCollection{}
if certificate != nil {
collection.Certificate = string(pem.EncodeToMemory(GetCertificatePEMBlock(certificate.Raw)))
@@ -63,7 +64,7 @@ func NewPEMCollection(certificate *x509.Certificate, privateKey interface{}, pri
if privateKey != nil {
var p *pem.Block
var err error
if privateKeyPassword != nil && len(privateKeyPassword) > 0 {
if len(privateKeyPassword) > 0 {
p, err = GetEncryptedPrivateKeyPEMBock(privateKey, privateKeyPassword)
} else {
p, err = GetPrivateKeyPEMBock(privateKey)
@@ -114,14 +115,20 @@ func PEMCollectionFromBytes(certBytes []byte, chainOrder ChainOption) (*PEMColle
collection, err = NewPEMCollection(chain[len(chain)-1], nil, nil)
if len(chain) > 1 && chainOrder != ChainOptionIgnore {
for _, caCert := range chain[:len(chain)-1] {
collection.AddChainElement(caCert)
err = collection.AddChainElement(caCert)
if err != nil {
return nil, err
}
}
}
default:
collection, err = NewPEMCollection(chain[0], nil, nil)
if len(chain) > 1 && chainOrder != ChainOptionIgnore {
for _, caCert := range chain[1:] {
collection.AddChainElement(caCert)
err = collection.AddChainElement(caCert)
if err != nil {
return nil, err
}
}
}
}
@@ -137,13 +144,13 @@ func PEMCollectionFromBytes(certBytes []byte, chainOrder ChainOption) (*PEMColle
}
//AddPrivateKey adds a Private Key to the PEMCollection. Note that the collection can only contain one private key
func (col *PEMCollection) AddPrivateKey(privateKey interface{}, privateKeyPassword []byte) error {
func (col *PEMCollection) AddPrivateKey(privateKey crypto.Signer, privateKeyPassword []byte) error { //todo: change to crypto.Signer type
if col.PrivateKey != "" {
return fmt.Errorf("The PEM Collection can only contain one private key")
}
var p *pem.Block
var err error
if privateKeyPassword != nil && len(privateKeyPassword) > 0 {
if len(privateKeyPassword) > 0 {
p, err = GetEncryptedPrivateKeyPEMBock(privateKey, privateKeyPassword)
} else {
p, err = GetPrivateKeyPEMBock(privateKey)
+99 -96
View File
@@ -19,10 +19,11 @@ package endpoint
import (
"crypto/x509"
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"log"
"regexp"
"sort"
"strings"
"github.com/Venafi/vcert/pkg/certificate"
)
// ConnectorType represents the available connectors
@@ -55,22 +56,30 @@ func (t ConnectorType) String() string {
// Connector provides a common interface for external communications with TPP or Venafi Cloud
type Connector interface {
// GetType returns a connector type (cloud/TPP/fake). Can be useful because some features are not supported by a Cloud connection.
GetType() ConnectorType
SetBaseURL(url string) (err error)
// SetZone sets a zone (by name) for requests with this connector.
SetZone(z string)
Ping() (err error)
Register(email string) (err error)
// Authenticate is usually called by NewClient and it is not required that you manually call it.
Authenticate(auth *Authentication) (err error)
ReadZoneConfiguration(zone string) (config *ZoneConfiguration, err error)
// ReadPolicyConfiguration returns information about zone policies. It can be used for checking request compatibility with policies.
ReadPolicyConfiguration() (policy *Policy, err error)
// ReadZoneConfiguration returns the zone configuration. A zone configuration includes zone policy and additional zone information.
ReadZoneConfiguration() (config *ZoneConfiguration, err error)
// GenerateRequest update certificate.Request with data from zone configuration.
GenerateRequest(config *ZoneConfiguration, req *certificate.Request) (err error)
RequestCertificate(req *certificate.Request, zone string) (requestID string, err error)
// RequestCertificate makes a request to the server with data for enrolling the certificate.
RequestCertificate(req *certificate.Request) (requestID string, err error)
// RetrieveCertificate immediately returns an enrolled certificate. Otherwise, RetrieveCertificate waits and retries during req.Timeout.
RetrieveCertificate(req *certificate.Request) (certificates *certificate.PEMCollection, err error)
RevokeCertificate(req *certificate.RevocationRequest) error
RenewCertificate(req *certificate.RenewalRequest) (requestID string, err error)
// ImportCertificate adds an existing certificate to Venafi Platform even if the certificate was not issued by Venafi Cloud or Venafi Platform. For information purposes.
ImportCertificate(req *certificate.ImportRequest) (*certificate.ImportResponse, error)
}
// Authentication provides a data construct for authentication data
// Authentication provides a struct for authentication data. Either specify User and Password for Trust Platform or specify an APIKey for Cloud.
type Authentication struct {
User string
Password string
@@ -99,28 +108,37 @@ func (err ErrCertificatePending) Error() string {
return fmt.Sprintf("Issuance is pending. You may try retrieving the certificate later using Pickup ID: %s\n\tStatus: %s", err.CertificateID, err.Status)
}
// ZoneConfiguration provides a common structure for certificate request data provided by the remote endpoint
type ZoneConfiguration struct {
Organization string
OrganizationLocked bool
OrganizationalUnit []string
Country string
CountryLocked bool
Province string
ProvinceLocked bool
Locality string
LocalityLocked bool
// Policy is struct that contains restrictions for certificates. Most of the fields contains list of regular expression.
// For satisfying policies, all values in the certificate field must match AT LEAST ONE regular expression in corresponding policy field.
type Policy struct {
SubjectCNRegexes []string
SubjectORegexes []string
SubjectOURegexes []string
SubjectSTRegexes []string
SubjectLRegexes []string
SubjectCRegexes []string
SANRegexes []string
// AllowedKeyConfigurations lists all allowed key configurations. Certificate key configuration have to be listed in this list.
// For example: If key has type RSA and length 2048 bit for satisfying the policy, that list must contain AT LEAST ONE configuration with type RSA and value 2048 in KeySizes list of this configuration.
AllowedKeyConfigurations []AllowedKeyConfiguration
KeySizeLocked bool
// DnsSanRegExs is a list of regular expressions that show allowable DNS names in SANs.
DnsSanRegExs []string
// IpSanRegExs is a list of regular expressions that show allowable DNS names in SANs.
IpSanRegExs []string
EmailSanRegExs []string
UriSanRegExs []string
UpnSanRegExs []string
AllowWildcards bool
AllowKeyReuse bool
}
// ZoneConfiguration provides a common structure for certificate request data provided by the remote endpoint
type ZoneConfiguration struct {
Organization string
OrganizationalUnit []string
Country string
Province string
Locality string
Policy
HashAlgorithm x509.SignatureAlgorithm
@@ -142,33 +160,34 @@ func NewZoneConfiguration() *ZoneConfiguration {
return &zc
}
// ValidateCertificateRequest validates the request against the zone configuration
func (z *ZoneConfiguration) ValidateCertificateRequest(request *certificate.Request) error {
if !isComponentValid(z.SubjectCNRegexes, []string{request.Subject.CommonName}) {
// ValidateCertificateRequest validates the request against the Policy
func (p *Policy) ValidateCertificateRequest(request *certificate.Request) error {
if !isComponentValid(p.SubjectCNRegexes, []string{request.Subject.CommonName}) {
return fmt.Errorf("The requested CN does not match any of the allowed CN regular expressions")
}
if !isComponentValid(z.SubjectORegexes, request.Subject.Organization) {
if !isComponentValid(p.SubjectORegexes, request.Subject.Organization) {
return fmt.Errorf("The requested Organization does not match any of the allowed Organization regular expressions")
}
if !isComponentValid(z.SubjectOURegexes, request.Subject.OrganizationalUnit) {
if !isComponentValid(p.SubjectOURegexes, request.Subject.OrganizationalUnit) {
return fmt.Errorf("The requested Organizational Unit does not match any of the allowed Organization Unit regular expressions")
}
if !isComponentValid(z.SubjectSTRegexes, request.Subject.Province) {
if !isComponentValid(p.SubjectSTRegexes, request.Subject.Province) {
return fmt.Errorf("The requested State/Province does not match any of the allowed State/Province regular expressions")
}
if !isComponentValid(z.SubjectLRegexes, request.Subject.Locality) {
if !isComponentValid(p.SubjectLRegexes, request.Subject.Locality) {
return fmt.Errorf("The requested Locality does not match any of the allowed Locality regular expressions")
}
if !isComponentValid(z.SubjectCRegexes, request.Subject.Country) {
if !isComponentValid(p.SubjectCRegexes, request.Subject.Country) {
return fmt.Errorf("The requested Country does not match any of the allowed Country regular expressions")
}
if !isComponentValid(z.SANRegexes, request.DNSNames) {
if !isComponentValid(p.DnsSanRegExs, request.DNSNames) {
return fmt.Errorf("The requested Subject Alternative Name does not match any of the allowed Country regular expressions")
}
//todo: add ip, email and over cheking
if z.AllowedKeyConfigurations != nil && len(z.AllowedKeyConfigurations) > 0 {
if p.AllowedKeyConfigurations != nil && len(p.AllowedKeyConfigurations) > 0 {
match := false
for _, keyConf := range z.AllowedKeyConfigurations {
for _, keyConf := range p.AllowedKeyConfigurations {
if keyConf.KeyType == request.KeyType {
if request.KeyLength > 0 {
for _, size := range keyConf.KeySizes {
@@ -194,88 +213,73 @@ func (z *ZoneConfiguration) ValidateCertificateRequest(request *certificate.Requ
}
func isComponentValid(regexes []string, component []string) bool {
if regexes != nil && len(regexes) > 0 && component != nil {
regexOk := false
for _, subReg := range regexes {
matchedAny := false
reg := regexp.MustCompile(subReg)
for _, c := range component {
if reg.FindStringIndex(c) != nil {
matchedAny = true
break
}
}
if matchedAny {
regexOk = true
if len(regexes) == 0 || len(component) == 0 {
return true
}
regexOk := false
for _, subReg := range regexes {
matchedAny := false
reg, err := regexp.Compile(subReg)
if err != nil {
log.Printf("Bad regexp: %s", subReg)
return false
}
for _, c := range component {
if reg.FindStringIndex(c) != nil {
matchedAny = true
break
}
}
return regexOk
}
return true
}
// UpdateCertificateRequest updates a certificate request based on the zone configurataion retrieved from the remote endpoint
func (z *ZoneConfiguration) UpdateCertificateRequest(request *certificate.Request) {
if (request.Subject.Organization == nil || len(request.Subject.Organization) == 0) && z.Organization != "" {
request.Subject.Organization = []string{z.Organization}
} else {
if z.OrganizationLocked && !strings.EqualFold(request.Subject.Organization[0], z.Organization) {
request.Subject.Organization = []string{z.Organization}
if matchedAny {
regexOk = true
break
}
}
if (request.Subject.OrganizationalUnit == nil || len(request.Subject.OrganizationalUnit) == 0) && z.OrganizationalUnit != nil {
return regexOk
}
// UpdateCertificateRequest updates a certificate request based on the zone configuration retrieved from the remote endpoint
func (z *ZoneConfiguration) UpdateCertificateRequest(request *certificate.Request) {
if len(request.Subject.Organization) == 0 && z.Organization != "" {
request.Subject.Organization = []string{z.Organization}
}
if len(request.Subject.OrganizationalUnit) == 0 && z.OrganizationalUnit != nil {
request.Subject.OrganizationalUnit = z.OrganizationalUnit
}
if (request.Subject.Country == nil || len(request.Subject.Country) == 0) && z.Country != "" {
if len(request.Subject.Country) == 0 && z.Country != "" {
request.Subject.Country = []string{z.Country}
} else {
if z.CountryLocked && !strings.EqualFold(request.Subject.Country[0], z.Country) {
request.Subject.Country = []string{z.Country}
}
}
if (request.Subject.Province == nil || len(request.Subject.Province) == 0) && z.Province != "" {
if len(request.Subject.Province) == 0 && z.Province != "" {
request.Subject.Province = []string{z.Province}
} else {
if z.ProvinceLocked && !strings.EqualFold(request.Subject.Province[0], z.Province) {
request.Subject.Province = []string{z.Province}
}
}
if (request.Subject.Locality == nil || len(request.Subject.Locality) == 0) && z.Locality != "" {
if len(request.Subject.Locality) == 0 && z.Locality != "" {
request.Subject.Locality = []string{z.Locality}
} else {
if z.LocalityLocked && !strings.EqualFold(request.Subject.Locality[0], z.Locality) {
request.Subject.Locality = []string{z.Locality}
}
}
if z.HashAlgorithm != 0 {
if z.HashAlgorithm != x509.UnknownSignatureAlgorithm {
request.SignatureAlgorithm = z.HashAlgorithm
} else {
request.SignatureAlgorithm = x509.SHA256WithRSA
}
if z.KeySizeLocked {
for _, keyConf := range z.AllowedKeyConfigurations {
if keyConf.KeyType == request.KeyType {
sort.Sort(sort.Reverse(sort.IntSlice(keyConf.KeySizes)))
request.KeyLength = keyConf.KeySizes[0]
}
}
} else if z.AllowedKeyConfigurations != nil {
if len(z.AllowedKeyConfigurations) != 0 {
foundMatch := false
for _, keyConf := range z.AllowedKeyConfigurations {
if keyConf.KeyType == request.KeyType {
foundMatch = true
switch request.KeyType {
case certificate.KeyTypeECDSA:
if z.AllowedKeyConfigurations[0].KeyCurves != nil {
request.KeyCurve = z.AllowedKeyConfigurations[0].KeyCurves[0]
if len(keyConf.KeyCurves) != 0 {
request.KeyCurve = keyConf.KeyCurves[0]
} else {
request.KeyCurve = certificate.EllipticCurveP256
request.KeyCurve = certificate.EllipticCurveDefault
}
case certificate.KeyTypeRSA:
if keyConf.KeySizes != nil {
if len(keyConf.KeySizes) != 0 {
sizeOK := false
for _, size := range keyConf.KeySizes {
if size == request.KeyLength {
@@ -293,18 +297,19 @@ func (z *ZoneConfiguration) UpdateCertificateRequest(request *certificate.Reques
}
}
if !foundMatch {
request.KeyType = z.AllowedKeyConfigurations[0].KeyType
configuration := z.AllowedKeyConfigurations[0]
request.KeyType = configuration.KeyType
switch request.KeyType {
case certificate.KeyTypeECDSA:
if z.AllowedKeyConfigurations[0].KeyCurves != nil {
request.KeyCurve = z.AllowedKeyConfigurations[0].KeyCurves[0]
if len(configuration.KeyCurves) != 0 {
request.KeyCurve = configuration.KeyCurves[0]
} else {
request.KeyCurve = certificate.EllipticCurveP256
request.KeyCurve = certificate.EllipticCurveDefault
}
case certificate.KeyTypeRSA:
if z.AllowedKeyConfigurations[0].KeySizes != nil {
sort.Sort(sort.Reverse(sort.IntSlice(z.AllowedKeyConfigurations[0].KeySizes)))
request.KeyLength = z.AllowedKeyConfigurations[0].KeySizes[0]
if len(configuration.KeySizes) != 0 {
sort.Sort(sort.Reverse(sort.IntSlice(configuration.KeySizes)))
request.KeyLength = configuration.KeySizes[0]
} else {
request.KeyLength = 2048
}
@@ -316,6 +321,4 @@ func (z *ZoneConfiguration) UpdateCertificateRequest(request *certificate.Reques
request.KeyLength = 2048
}
}
return
}
+85 -13
View File
@@ -16,14 +16,19 @@
package cloud
import "time"
import (
"github.com/Venafi/vcert/pkg/endpoint"
"regexp"
"strings"
"time"
)
type certificatePolicy struct {
CertificatePolicyType certificatePolicyType `json:"certificatePolicyType,omitempty"`
ID string `json:"id,omitempty"`
CompanyID string `json:"companyId,omitempty"`
Name string `json:"name,omitempty"`
SystemGenerated bool `json:"systemGeneratedate,omitempty"`
SystemGenerated bool `json:"systemGenerated,omitempty"`
CreationDateString string `json:"creationDate,omitempty"`
CreationDate time.Time `json:"-"`
CertificateProviderID string `json:"certificateProviderId,omitempty"`
@@ -32,7 +37,7 @@ type certificatePolicy struct {
SubjectOURegexes []string `json:"subjectOURegexes,omitempty"`
SubjectSTRegexes []string `json:"subjectSTRegexes,omitempty"`
SubjectLRegexes []string `json:"subjectLRegexes,omitempty"`
SubjectCRegexes []string `json:"subjectCRegexes,omitempty"`
SubjectCRegexes []string `json:"subjectCValues,omitempty"`
SANRegexes []string `json:"sanRegexes,omitempty"`
KeyTypes []allowedKeyType `json:"keyTypes,omitempty"`
KeyReuse bool `json:"keyReuse,omitempty"`
@@ -47,20 +52,87 @@ type certificatePolicyType string
const (
certificatePolicyTypeIdentity certificatePolicyType = "CERTIFICATE_IDENTITY"
certificatePolicyTypeUse = "CERTIFICATE_USE"
certificatePolicyTypeUse certificatePolicyType = "CERTIFICATE_USE"
)
type keyType string
const (
keyTypeRSA keyType = "RSA"
keyTypeDSA = "DSA"
keyTypeEC = "EC"
keyTypeGost3410 = "GOST3410"
keyTypeECGost3410 = "ECGOST3410"
keyTypeReserved3 = "RESERVED3"
keyTypeUnknown = "UNKNOWN"
)
func (cp certificatePolicy) toPolicy() (p endpoint.Policy) {
addStartEnd := func(s string) string {
if !strings.HasPrefix(s, "^") {
s = "^" + s
}
if !strings.HasSuffix(s, "$") {
s = s + "$"
}
return s
}
addStartEndToArray := func(ss []string) []string {
a := make([]string, len(ss))
for i, s := range ss {
a[i] = addStartEnd(s)
}
return a
}
p.SubjectCNRegexes = addStartEndToArray(cp.SubjectCNRegexes)
p.SubjectOURegexes = addStartEndToArray(cp.SubjectOURegexes)
p.SubjectCRegexes = addStartEndToArray(cp.SubjectCRegexes)
p.SubjectSTRegexes = addStartEndToArray(cp.SubjectSTRegexes)
p.SubjectLRegexes = addStartEndToArray(cp.SubjectLRegexes)
p.SubjectORegexes = addStartEndToArray(cp.SubjectORegexes)
p.DnsSanRegExs = addStartEndToArray(cp.SANRegexes)
p.AllowKeyReuse = cp.KeyReuse
allowWildCards := false
for _, s := range p.SubjectCNRegexes {
if strings.HasPrefix(s, "^.*") {
allowWildCards = true
}
}
if !allowWildCards {
for _, s := range p.DnsSanRegExs {
if strings.HasPrefix(s, "^.*") {
allowWildCards = true
}
}
}
p.AllowWildcards = allowWildCards
for _, kt := range cp.KeyTypes {
keyConfiguration := endpoint.AllowedKeyConfiguration{}
if err := keyConfiguration.KeyType.Set(string(kt.KeyType)); err != nil {
panic(err)
}
keyConfiguration.KeySizes = kt.KeyLengths[:]
p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, keyConfiguration)
}
return
}
func isNotRegexp(s string) bool {
matched, err := regexp.MatchString(`[a-zA-Z0-9 ]+`, s)
if !matched || err != nil {
return false
}
return true
}
func (cp certificatePolicy) toZoneConfig(zc *endpoint.ZoneConfiguration) {
if len(cp.SubjectCRegexes) > 0 && isNotRegexp(cp.SubjectCRegexes[0]) {
zc.Country = cp.SubjectCRegexes[0]
}
if len(cp.SubjectORegexes) > 0 && isNotRegexp(cp.SubjectORegexes[0]) {
zc.Organization = cp.SubjectORegexes[0]
}
if len(cp.SubjectSTRegexes) > 0 && isNotRegexp(cp.SubjectSTRegexes[0]) {
zc.Province = cp.SubjectSTRegexes[0]
}
if len(cp.SubjectLRegexes) > 0 && isNotRegexp(cp.SubjectLRegexes[0]) {
zc.Locality = cp.SubjectLRegexes[0]
}
for _, ou := range cp.SubjectOURegexes {
if isNotRegexp(ou) {
zc.OrganizationalUnit = append(zc.OrganizationalUnit, ou)
}
}
}
/*
"signatureAlgorithm":{"type":"string","enum":["MD2_WITH_RSA_ENCRYPTION","MD5_WITH_RSA_ENCRYPTION","SHA1_WITH_RSA_ENCRYPTION","SHA1_WITH_RSA_ENCRYPTION2","SHA256_WITH_RSA_ENCRYPTION","SHA384_WITH_RSA_ENCRYPTION","SHA512_WITH_RSA_ENCRYPTION","ID_DSA_WITH_SHA1","dsaWithSHA1","EC_DSA_WITH_SHA1","EC_DSA_WITH_SHA224","EC_DSA_WITH_SHA256","EC_DSA_WITH_SHA384","EC_DSA_WITH_SHA512","UNKNOWN","SHA1_WITH_RSAandMGF1","GOST_R3411_94_WITH_GOST_R3410_2001","GOST_R3411_94_WITH_GOST_R3410_94"]},
+113 -47
View File
@@ -17,15 +17,19 @@
package cloud
import (
"bytes"
"crypto/sha1"
"encoding/json"
"encoding/pem"
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
"io"
"io/ioutil"
"log"
"net/http"
"regexp"
"strings"
"time"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
)
type apiKey struct {
@@ -93,13 +97,49 @@ type CertificateStatusErrorInformation struct {
Args []string `json:"args,omitempty"`
}
type importRequestEndpointCert struct {
Certificate string `json:"certificate"`
Fingerprint string `json:"fingerprint"`
}
type importRequestEndpointProtocol struct {
Certificates []string `json:"certificates"`
Ciphers []interface{} `json:"ciphers"` //todo: check type
Protocol string `json:"protocol"`
}
type importRequestEndpoint struct {
Alpn bool `json:"alpn"`
Certificates []importRequestEndpointCert `json:"certificates"`
ClientRenegotiation bool `json:"clientRenegotiation"`
Drown bool `json:"drown"`
Heartbleed bool `json:"heartbleed"`
Host string `json:"host"`
HSTS bool `json:"hsts"`
IP string `json:"ip"`
LogJam int `json:"logJam"`
Npn bool `json:"npn"`
OCSP int `json:"ocsp"` //default 1
Poodle bool `json:"poodle"`
PoodleTls bool `json:"poodleTls"`
Port int `json:"port"`
Protocols []importRequestEndpointProtocol `json:"protocols"`
SecureRenegotiation bool `json:"secureRenegotiation"`
Sloth bool `json:"sloth"`
}
type importRequest struct {
ZoneName string `json:"zoneName"`
NetworkID string `json:"networkId"`
Endpoints []importRequestEndpoint `json:"endpoints"`
}
//GenerateRequest generates a CertificateRequest based on the zone configuration, and returns the request along with the private key.
func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *certificate.Request) (err error) {
switch req.CsrOrigin {
case certificate.LocalGeneratedCSR:
var pk interface{}
if config == nil {
config, err = c.ReadZoneConfiguration(c.zone)
config, err = c.ReadZoneConfiguration()
if err != nil {
return fmt.Errorf("could not read zone configuration: %s", err)
}
@@ -109,33 +149,18 @@ func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *cer
return err
}
config.UpdateCertificateRequest(req)
switch req.KeyType {
case certificate.KeyTypeECDSA:
pk, err = certificate.GenerateECDSAPrivateKey(req.KeyCurve)
case certificate.KeyTypeRSA:
pk, err = certificate.GenerateRSAPrivateKey(req.KeyLength)
default:
return fmt.Errorf("Unable to generate certificate request, key type %s is not supported", req.KeyType.String())
}
if err != nil {
if err := req.GeneratePrivateKey(); err != nil {
return err
}
req.PrivateKey = pk
err = certificate.GenerateRequest(req, pk)
if err != nil {
return err
}
req.CSR = pem.EncodeToMemory(certificate.GetCertificateRequestPEMBlock(req.CSR))
return nil
err = req.GenerateCSR()
return
case certificate.UserProvidedCSR:
if req.CSR == nil || len(req.CSR) == 0 {
if len(req.GetCSR()) == 0 {
return fmt.Errorf("CSR was supposed to be provided by user, but it's empty")
}
return nil
case certificate.ServiceGeneratedCSR:
req.CSR = nil
return nil
default:
@@ -143,32 +168,68 @@ func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *cer
}
}
//SetBaseURL allows overriding the default URL used to communicate with Venafi Cloud
func (c *Connector) SetBaseURL(url string) error {
if url == "" {
return fmt.Errorf("base URL cannot be empty")
}
modified := strings.ToLower(url)
reg := regexp.MustCompile("^http(|s)://")
if reg.FindStringIndex(modified) == nil {
modified = "https://" + modified
} else {
modified = reg.ReplaceAllString(modified, "https://")
}
reg = regexp.MustCompile("/v1(|/)$")
if reg.FindStringIndex(modified) == nil {
modified += "v1/"
} else {
modified = reg.ReplaceAllString(modified, "/v1/")
}
c.baseURL = modified
return nil
}
func (c *Connector) getURL(resource urlResource) string {
return fmt.Sprintf("%s%s", c.baseURL, resource)
}
func (c *Connector) request(method string, url string, data interface{}, authNotRequired ...bool) (statusCode int, statusText string, body []byte, err error) {
if c.user == nil || c.user.Company == nil {
if !(len(authNotRequired) == 1 && authNotRequired[0]) {
err = fmt.Errorf("Must be autheticated to retieve certificate")
return
}
}
var payload io.Reader
var b []byte
if method == "POST" {
b, _ = json.Marshal(data)
payload = bytes.NewReader(b)
}
r, err := http.NewRequest(method, url, payload)
if err != nil {
return
}
if c.apiKey != "" {
r.Header.Add("tppl-api-key", c.apiKey)
}
if method == "POST" {
r.Header.Add("Accept", "application/json")
r.Header.Add("content-type", "application/json")
} else {
r.Header.Add("Accept", "*/*")
}
r.Header.Add("cache-control", "no-cache")
res, err := http.DefaultClient.Do(r)
if res != nil {
statusCode = res.StatusCode
statusText = res.Status
}
if err != nil {
return
}
defer res.Body.Close()
body, err = ioutil.ReadAll(res.Body)
// Do not enable trace in production
trace := false // IMPORTANT: sensitive information can be diclosured
// I hope you know what are you doing
if trace {
log.Println("#################")
if method == "POST" {
log.Printf("JSON sent for %s\n%s\n", url, string(b))
} else {
log.Printf("%s request sent to %s\n", method, url)
}
log.Printf("Response:\n%s\n", string(body))
} else if c.verbose {
log.Printf("Got %s status for %s %s\n", statusText, method, url)
}
return
}
func parseUserDetailsResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*userDetails, error) {
if httpStatusCode == expectedStatusCode {
resp, err := parseUserDetailsData(body)
@@ -350,3 +411,8 @@ func parseCertificateRequestData(b []byte) (*certificateRequestResponse, error)
func newPEMCollectionFromResponse(data []byte, chainOrder certificate.ChainOption) (*certificate.PEMCollection, error) {
return certificate.PEMCollectionFromBytes(data, chainOrder)
}
func certThumprint(asn1 []byte) string {
h := sha1.Sum(asn1)
return strings.ToUpper(fmt.Sprintf("%x", h))
}
+8 -20
View File
@@ -17,8 +17,6 @@
package cloud
import (
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
"time"
)
@@ -41,7 +39,7 @@ type zone struct {
CertificatePolicyIDs certificatePolicyID `json:"certificatePolicyIds,omitempty"`
DefaultCertificateIdentityPolicy string `json:"defaultCertificateIdentityPolicyId,omitempty"`
DefaultCertificateUsePolicy string `json:"defaultCertificateUsePolicyId,omitempty"`
SystemGenerated bool `json:"systemGeneratedate,omitempty"`
SystemGenerated bool `json:"systemGenerated,omitempty"`
CreationDateString string `json:"creationDate,omitempty"`
CreationDate time.Time `json:"-"`
}
@@ -51,24 +49,14 @@ type certificatePolicyID struct {
CertificateUse []string `json:"CERTIFICATE_USE,omitempty"`
}
func (z *zone) GetZoneConfiguration(ud *userDetails, policy *certificatePolicy) *endpoint.ZoneConfiguration {
zoneConfig := endpoint.ZoneConfiguration{}
if policy != nil {
if policy.KeyTypes != nil {
certKeyType := certificate.KeyTypeRSA
for _, kt := range policy.KeyTypes {
certKeyType.Set(fmt.Sprintf("%s", kt.KeyType))
keyConfiguration := endpoint.AllowedKeyConfiguration{}
keyConfiguration.KeyType = certKeyType
for _, size := range kt.KeyLengths {
keyConfiguration.KeySizes = append(keyConfiguration.KeySizes, size)
}
zoneConfig.AllowedKeyConfigurations = append(zoneConfig.AllowedKeyConfigurations, keyConfiguration)
}
}
func (z *zone) getZoneConfiguration(ud *userDetails, policy *certificatePolicy) (zoneConfig *endpoint.ZoneConfiguration) {
zoneConfig = endpoint.NewZoneConfiguration()
if policy == nil {
return
}
return &zoneConfig
zoneConfig.Policy = policy.toPolicy()
policy.toZoneConfig(zoneConfig)
return
}
const (
+251 -331
View File
@@ -17,17 +17,18 @@
package cloud
import (
"bytes"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
"io/ioutil"
"log"
"net/http"
"regexp"
"strings"
"time"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
)
const apiURL = "api.venafi.cloud/v1/"
@@ -35,26 +36,29 @@ const apiURL = "api.venafi.cloud/v1/"
type urlResource string
const (
urlResourceUserAccounts urlResource = "useraccounts"
urlResourcePing = "ping"
urlResourceZones = "zones"
urlResourceZoneByTag = urlResourceZones + "/tag/%s"
urlResourceCertificatePolicies = "certificatepolicies"
urlResourcePoliciesByID = urlResourceCertificatePolicies + "%s"
urlResourcePoliciesForZoneByID = urlResourceCertificatePolicies + "?zoneId=%s"
urlResourceCertificateRequests = "certificaterequests"
urlResourceCertificateStatus = urlResourceCertificateRequests + "/%s"
urlResourceCertificateRetrieve = urlResourceCertificateRequests + "/%s/certificate"
urlResourceCertificateSearch = "certificatesearch"
urlResourceManagedCertificates = "managedcertificates"
urlResourceManagedCertificateById = urlResourceManagedCertificates + "/%s"
urlResourceUserAccounts urlResource = "useraccounts"
urlResourcePing = "ping"
urlResourceZones = "zones"
urlResourceZoneByTag = urlResourceZones + "/tag/%s"
urlResourceCertificatePolicies = "certificatepolicies"
urlResourcePoliciesByID = urlResourceCertificatePolicies + "/%s"
urlResourcePoliciesForZoneByID = urlResourceCertificatePolicies + "?zoneId=%s"
urlResourceCertificateRequests = "certificaterequests"
urlResourceCertificateStatus = urlResourceCertificateRequests + "/%s"
urlResourceCertificateRetrieveViaCSR = urlResourceCertificateRequests + "/%s/certificate"
urlResourceCertificateRetrieve = "certificates/%s"
urlResourceCertificateRetrievePem = urlResourceCertificateRetrieve + "/encoded"
urlResourceCertificateSearch = "certificatesearch"
urlResourceManagedCertificates = "managedcertificates"
urlResourceManagedCertificateByID = urlResourceManagedCertificates + "/%s"
urlResourceDiscovery = "discovery"
)
type condorChainOption string
const (
condorChainOptionRootFirst condorChainOption = "ROOT_FIRST"
condorChainOptionRootLast = "EE_FIRST"
condorChainOptionRootLast condorChainOption = "EE_FIRST"
)
// Connector contains the base data needed to communicate with the Venafi Cloud servers
@@ -68,10 +72,37 @@ type Connector struct {
}
// NewConnector creates a new Venafi Cloud Connector object used to communicate with Venafi Cloud
func NewConnector(verbose bool, trust *x509.CertPool) *Connector {
c := Connector{verbose: verbose, trust: trust}
c.SetBaseURL(apiURL)
return &c
func NewConnector(url string, zone string, verbose bool, trust *x509.CertPool) (*Connector, error) {
c := Connector{verbose: verbose, trust: trust, zone: zone}
var err error
c.baseURL, err = normalizeURL(url)
if err != nil {
return nil, err
}
return &c, nil
}
//normalizeURL allows overriding the default URL used to communicate with Venafi Cloud
func normalizeURL(url string) (normalizedURL string, err error) {
if url == "" {
url = apiURL
//return "", fmt.Errorf("base URL cannot be empty")
}
modified := strings.ToLower(url)
reg := regexp.MustCompile("^http(|s)://")
if reg.FindStringIndex(modified) == nil {
modified = "https://" + modified
} else {
modified = reg.ReplaceAllString(modified, "https://")
}
reg = regexp.MustCompile("/v1(|/)$")
if reg.FindStringIndex(modified) == nil {
modified += "v1/"
} else {
modified = reg.ReplaceAllString(modified, "/v1/")
}
normalizedURL = modified
return normalizedURL, nil
}
func (c *Connector) SetZone(z string) {
@@ -82,108 +113,59 @@ func (c *Connector) GetType() endpoint.ConnectorType {
return endpoint.ConnectorTypeCloud
}
//Ping attempts to connect to the Venafi Cloud API and returns an errror if it cannot
// Ping attempts to connect to the Venafi Cloud API and returns an errror if it cannot
func (c *Connector) Ping() (err error) {
url := c.getURL(urlResourcePing)
resp, err := http.Get(url)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("Unexpected status code on Venafi Cloud ping. Status: %d %s", resp.StatusCode, resp.Status)
}
return err
return nil
}
//Authenticate authenticates the user with Venafi Cloud using the provided API Key
// Authenticate authenticates the user with Venafi Cloud using the provided API Key
func (c *Connector) Authenticate(auth *endpoint.Authentication) (err error) {
if auth == nil {
return fmt.Errorf("failed to authenticate: missing credentials")
}
c.apiKey = auth.APIKey
url := c.getURL(urlResourceUserAccounts)
b := []byte{}
reader := bytes.NewReader(b)
request, err := http.NewRequest("GET", url, reader)
statusCode, status, body, err := c.request("GET", url, nil, true)
if err != nil {
return err
}
request.Header.Add("tppl-api-key", c.apiKey)
resp, err := http.DefaultClient.Do(request)
ud, err := parseUserDetailsResult(http.StatusOK, statusCode, status, body)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
ud, err := parseUserDetailsResult(http.StatusOK, resp.StatusCode, resp.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
}
return err
return
}
c.user = ud
return nil
return
}
//Register registers a new user with Venafi Cloud
func (c *Connector) Register(email string) (err error) {
b, err := json.Marshal(userAccount{Username: email, UserAccountType: "API"})
url := c.getURL(urlResourceUserAccounts)
reader := bytes.NewReader(b)
resp, err := http.Post(url, "application/json", reader)
func (c *Connector) ReadPolicyConfiguration() (policy *endpoint.Policy, err error) {
config, err := c.ReadZoneConfiguration()
if err != nil {
return err
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
//the user has already been registered and there is nothing to parse
if resp.StatusCode == http.StatusAccepted {
return nil
}
ud, err := parseUserDetailsResult(http.StatusCreated, resp.StatusCode, resp.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
}
return err
}
c.user = ud
return nil
policy = &config.Policy
return
}
//ReadZoneConfiguration reads the Zone information needed for generating and requesting a certificate from Venafi Cloud
func (c *Connector) ReadZoneConfiguration(zone string) (config *endpoint.ZoneConfiguration, err error) {
z, err := c.getZoneByTag(zone)
// ReadZoneConfiguration reads the Zone information needed for generating and requesting a certificate from Venafi Cloud
func (c *Connector) ReadZoneConfiguration() (config *endpoint.ZoneConfiguration, err error) {
if c.zone == "" {
return nil, fmt.Errorf("empty zone name")
}
z, err := c.getZoneByTag(c.zone)
if err != nil {
return nil, err
}
p, err := c.getPoliciesByID([]string{z.DefaultCertificateIdentityPolicy, z.DefaultCertificateUsePolicy})
config = z.GetZoneConfiguration(c.user, p)
if err != nil {
return
}
config = z.getZoneConfiguration(c.user, p)
return config, nil
}
//RequestCertificate submits the CSR to the Venafi Cloud API for processing
func (c *Connector) RequestCertificate(req *certificate.Request, zone string) (requestID string, err error) {
if zone == "" {
zone = c.zone
}
// RequestCertificate submits the CSR to the Venafi Cloud API for processing
func (c *Connector) RequestCertificate(req *certificate.Request) (requestID string, err error) {
if req.CsrOrigin == certificate.ServiceGeneratedCSR {
return "", fmt.Errorf("service generated CSR is not supported by Saas service")
}
@@ -192,34 +174,18 @@ func (c *Connector) RequestCertificate(req *certificate.Request, zone string) (r
if c.user == nil || c.user.Company == nil {
return "", fmt.Errorf("Must be autheticated to request a certificate")
}
z, err := c.getZoneByTag(zone)
if err != nil {
return "", err
}
b, _ := json.Marshal(certificateRequest{ZoneID: z.ID, CSR: string(req.CSR)})
reader := bytes.NewReader(b)
request, err := http.NewRequest("POST", url, reader)
if err != nil {
return "", err
}
request.Header.Add("tppl-api-key", c.apiKey)
request.Header.Add("content-type", "application/json")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
z, err := c.getZoneByTag(c.zone)
if err != nil {
return "", err
}
cr, err := parseCertificateRequestResult(resp.StatusCode, resp.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
}
statusCode, status, body, err := c.request("POST", url, certificateRequest{ZoneID: z.ID, CSR: string(req.GetCSR())})
if err != nil {
return "", err
}
cr, err := parseCertificateRequestResult(statusCode, status, body)
if err != nil {
return "", err
}
requestID = cr.CertificateRequests[0].ID
@@ -227,61 +193,41 @@ func (c *Connector) RequestCertificate(req *certificate.Request, zone string) (r
return requestID, nil
}
func (c *Connector) getCertificateStatus(requestID string) (*certificateStatus, error) {
var err error
func (c *Connector) getCertificateStatus(requestID string) (certStatus *certificateStatus, err error) {
url := c.getURL(urlResourceCertificateStatus)
if c.user == nil || c.user.Company == nil {
err = fmt.Errorf("Must be autheticated to retieve certificate")
return nil, err
}
url = fmt.Sprintf(url, requestID)
b := []byte{}
reader := bytes.NewReader(b)
request, err := http.NewRequest("GET", url, reader)
statusCode, _, body, err := c.request("GET", url, nil)
if err != nil {
return nil, err
}
request.Header.Add("tppl-api-key", c.apiKey)
request.Header.Add("Accept", "application/json")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
switch resp.StatusCode {
case http.StatusOK:
var data = &certificateStatus{}
err = json.Unmarshal(body, data)
if statusCode == http.StatusOK {
certStatus = &certificateStatus{}
err = json.Unmarshal(body, certStatus)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate request status response: %s", err)
}
return data, nil
default:
if body != nil {
respErrors, err := parseResponseErrors(body)
if err == nil {
respError := fmt.Sprintf("Unexpected status code on Venafi Cloud certificate search. Status: %d\n", resp.StatusCode)
for _, e := range respErrors {
respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message)
}
return nil, fmt.Errorf(respError)
}
}
return nil, fmt.Errorf("Unexpected status code on Venafi Cloud certificate search. Status: %d", resp.StatusCode)
return
}
respErrors, err := parseResponseErrors(body)
if err == nil {
respError := fmt.Sprintf("Unexpected status code on Venafi Cloud certificate search. Status: %d\n", statusCode)
for _, e := range respErrors {
respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message)
}
return nil, fmt.Errorf(respError)
}
return nil, fmt.Errorf("Unexpected status code on Venafi Cloud certificate search. Status: %d", statusCode)
}
//RetrieveCertificate retrieves the certificate for the specified ID
// RetrieveCertificate retrieves the certificate for the specified ID
func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates *certificate.PEMCollection, err error) {
if req.FetchPrivateKey {
return nil, fmt.Errorf("Failed to retrieve private key from Venafi Cloud service: not supported")
}
if req.PickupID == "" && req.Thumbprint != "" {
if req.PickupID == "" && req.CertID == "" && req.Thumbprint != "" {
// search cert by Thumbprint and fill pickupID
var certificateRequestId string
searchResult, err := c.searchCertificatesByFingerprint(req.Thumbprint)
@@ -299,7 +245,12 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates
if certificateRequestId != "" && certificateRequestId != c.CertificateRequestId {
isOnlyOneCertificateRequestId = false
}
certificateRequestId = c.CertificateRequestId
if c.CertificateRequestId != "" {
certificateRequestId = c.CertificateRequestId
}
if c.Id != "" {
req.CertID = c.Id
}
}
if !isOnlyOneCertificateRequestId {
return nil, fmt.Errorf("More than one CertificateRequestId was found with the same Fingerprint: %s", reqIds)
@@ -309,66 +260,78 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates
}
startTime := time.Now()
for {
status, err := c.getCertificateStatus(req.PickupID)
if err != nil {
return nil, fmt.Errorf("unable to retrieve: %s", err)
//Wait for certificate to be issued by checking it's PickupID
//If certID is filled then certificate should be already issued.
if req.CertID == "" {
for {
if req.PickupID == "" {
break
}
status, err := c.getCertificateStatus(req.PickupID)
if err != nil {
return nil, fmt.Errorf("unable to retrieve: %s", err)
}
if status.Status == "ISSUED" {
break // to fetch the cert itself
} else if status.Status == "FAILED" {
return nil, fmt.Errorf("Failed to retrieve certificate. Status: %v", status)
}
// status.Status == "REQUESTED" || status.Status == "PENDING"
if req.Timeout == 0 {
return nil, endpoint.ErrCertificatePending{CertificateID: req.PickupID, Status: status.Status}
}
if time.Now().After(startTime.Add(req.Timeout)) {
return nil, endpoint.ErrRetrieveCertificateTimeout{CertificateID: req.PickupID}
}
// fmt.Printf("pending... %s\n", status.Status)
time.Sleep(2 * time.Second)
}
if status.Status == "ISSUED" {
break // to fetch the cert itself
} else if status.Status == "FAILED" {
return nil, fmt.Errorf("Failed to retrieve certificate. Status: %v", status)
}
// status.Status == "REQUESTED" || status.Status == "PENDING"
if req.Timeout == 0 {
return nil, endpoint.ErrCertificatePending{CertificateID: req.PickupID, Status: status.Status}
}
if time.Now().After(startTime.Add(req.Timeout)) {
return nil, endpoint.ErrRetrieveCertificateTimeout{CertificateID: req.PickupID}
}
// fmt.Printf("pending... %s\n", status.Status)
time.Sleep(2 * time.Second)
}
url := c.getURL(urlResourceCertificateRetrieve)
if c.user == nil || c.user.Company == nil {
return nil, fmt.Errorf("Must be autheticated to retieve certificate")
}
url = fmt.Sprintf(url, req.PickupID)
url += "?chainOrder=%s&format=PEM"
switch req.ChainOption {
case certificate.ChainOptionRootFirst:
url = fmt.Sprintf(url, condorChainOptionRootFirst)
default:
url = fmt.Sprintf(url, condorChainOptionRootLast)
}
b := []byte{}
reader := bytes.NewReader(b)
request, err := http.NewRequest("GET", url, reader)
if err != nil {
return nil, err
}
request.Header.Add("tppl-api-key", c.apiKey)
request.Header.Add("Accept", "text/plain")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode == http.StatusOK {
return newPEMCollectionFromResponse(body, req.ChainOption)
} else if resp.StatusCode == http.StatusConflict { // Http Status Code 409 means the certificate has not been signed by the ca yet.
return nil, endpoint.ErrCertificatePending{CertificateID: req.PickupID}
} else {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
switch {
case req.CertID != "":
url := c.getURL(urlResourceCertificateRetrievePem)
url = fmt.Sprintf(url, req.CertID)
statusCode, status, body, err := c.request("GET", url, nil)
if err != nil {
return nil, err
}
if statusCode != http.StatusOK {
return nil, fmt.Errorf("Failed to retrieve certificate. StatusCode: %d -- Status: %s -- Server Data: %s", statusCode, status, body)
}
return newPEMCollectionFromResponse(body, certificate.ChainOptionIgnore)
case req.PickupID != "":
url := c.getURL(urlResourceCertificateRetrieveViaCSR)
url = fmt.Sprintf(url, req.PickupID)
url += "?chainOrder=%s&format=PEM"
switch req.ChainOption {
case certificate.ChainOptionRootFirst:
url = fmt.Sprintf(url, condorChainOptionRootFirst)
default:
url = fmt.Sprintf(url, condorChainOptionRootLast)
}
statusCode, status, body, err := c.request("GET", url, nil)
if err != nil {
return nil, err
}
if statusCode == http.StatusOK {
certificates, err = newPEMCollectionFromResponse(body, req.ChainOption)
if err != nil {
return nil, err
}
err = req.CheckCertificate(certificates.Certificate)
return certificates, err
} else if statusCode == http.StatusConflict { // Http Status Code 409 means the certificate has not been signed by the ca yet.
return nil, endpoint.ErrCertificatePending{CertificateID: req.PickupID}
} else {
return nil, fmt.Errorf("Failed to retrieve certificate. StatusCode: %d -- Status: %s -- Server Data: %s", statusCode, status, body) //todo:remove body from err
}
return nil, fmt.Errorf("Failed to retrieve certificate. StatusCode: %d -- Status: %s -- Server Data: %s", resp.StatusCode, resp.Status, body)
}
return nil, fmt.Errorf("Couldn't retrieve certificate because both PickupID and CertId are empty")
}
// RevokeCertificate attempts to revoke the certificate
@@ -423,7 +386,7 @@ func (c *Connector) RenewCertificate(renewReq *certificate.RenewalRequest) (requ
return "", fmt.Errorf("failed to submit renewal request for certificate: ManagedCertificateId is empty, certificate status is %s", previousRequest.Status)
}
if managedCertificateId == "" {
if zoneId == "" {
return "", fmt.Errorf("failed to submit renewal request for certificate: ZoneId is empty, certificate status is %s", previousRequest.Status)
}
@@ -451,40 +414,20 @@ func (c *Connector) RenewCertificate(renewReq *certificate.RenewalRequest) (requ
return "", fmt.Errorf("Must be autheticated to request a certificate")
}
req := certificateRequest{
ZoneID: zoneId,
ExistingManagedCertificateId: managedCertificateId,
}
if renewReq.CertificateRequest != nil && 0 < len(renewReq.CertificateRequest.CSR) {
req.CSR = string(renewReq.CertificateRequest.CSR)
req := certificateRequest{ZoneID: zoneId, ExistingManagedCertificateId: managedCertificateId}
if renewReq.CertificateRequest != nil && len(renewReq.CertificateRequest.GetCSR()) != 0 {
req.CSR = string(renewReq.CertificateRequest.GetCSR())
req.ReuseCSR = false
} else {
req.ReuseCSR = true
}
b, _ := json.Marshal(req)
reader := bytes.NewReader(b)
request, err := http.NewRequest("POST", url, reader)
statusCode, status, body, err := c.request("POST", url, req)
if err != nil {
return "", err
}
request.Header.Add("tppl-api-key", c.apiKey)
request.Header.Add("content-type", "application/json")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
return
}
cr, err := parseCertificateRequestResult(resp.StatusCode, resp.Status, body)
cr, err := parseCertificateRequestResult(statusCode, status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
}
return "", fmt.Errorf("Failed to renew certificate: %s", err)
}
return cr.CertificateRequests[0].ID, nil
@@ -496,65 +439,33 @@ func (c *Connector) getZoneByTag(tag string) (*zone, error) {
return nil, fmt.Errorf("Must be autheticated to read the zone configuration")
}
url = fmt.Sprintf(url, tag)
b := []byte{}
reader := bytes.NewReader(b)
request, err := http.NewRequest("GET", url, reader)
statusCode, status, body, err := c.request("GET", url, nil)
if err != nil {
return nil, err
}
request.Header.Add("tppl-api-key", c.apiKey)
resp, err := http.DefaultClient.Do(request)
z, err := parseZoneConfigurationResult(statusCode, status, body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
z, err := parseZoneConfigurationResult(resp.StatusCode, resp.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
}
return nil, err
}
return z, nil
}
func (c *Connector) getPoliciesByID(ids []string) (*certificatePolicy, error) {
policy := new(certificatePolicy)
url := c.getURL(urlResourcePoliciesByID)
if c.user == nil {
return nil, fmt.Errorf("Must be autheticated to read the zone configuration")
}
for _, id := range ids {
url := c.getURL(urlResourcePoliciesByID)
url = fmt.Sprintf(url, id)
b := []byte{}
reader := bytes.NewReader(b)
request, err := http.NewRequest("GET", url, reader)
statusCode, status, body, err := c.request("GET", url, nil)
if err != nil {
return nil, err
}
request.Header.Add("tppl-api-key", c.apiKey)
resp, err := http.DefaultClient.Do(request)
p, err := parseCertificatePolicyResult(statusCode, status, body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
p, err := parseCertificatePolicyResult(resp.StatusCode, resp.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
}
return nil, err
}
switch p.CertificatePolicyType {
case certificatePolicyTypeIdentity:
policy.SubjectCNRegexes = p.SubjectCNRegexes
@@ -564,11 +475,9 @@ func (c *Connector) getPoliciesByID(ids []string) (*certificatePolicy, error) {
policy.SubjectLRegexes = p.SubjectLRegexes
policy.SubjectCRegexes = p.SubjectCRegexes
policy.SANRegexes = p.SANRegexes
break
case certificatePolicyTypeUse:
policy.KeyTypes = p.KeyTypes
policy.KeyReuse = p.KeyReuse
break
}
}
return policy, nil
@@ -579,36 +488,11 @@ func (c *Connector) searchCertificates(req *SearchRequest) (*CertificateSearchRe
var err error
url := c.getURL(urlResourceCertificateSearch)
if c.user == nil || c.user.Company == nil {
err = fmt.Errorf("Must be autheticated")
return nil, err
}
b, _ := json.Marshal(req)
reader := bytes.NewReader(b)
request, err := http.NewRequest("POST", url, reader)
statusCode, _, body, err := c.request("POST", url, req)
if err != nil {
return nil, err
}
request.Header.Add("tppl-api-key", c.apiKey)
request.Header.Add("content-type", "application/json")
request.Header.Add("accept", "application/json")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if c.verbose {
fmt.Printf("REQ: %s\n", b)
fmt.Printf("RES: %s\n", body)
}
searchResult, err := ParseCertificateSearchResponse(resp.StatusCode, body)
searchResult, err := ParseCertificateSearchResponse(statusCode, body)
if err != nil {
return nil, err
}
@@ -653,35 +537,14 @@ type managedCertificate struct {
func (c *Connector) getManagedCertificate(managedCertId string) (*managedCertificate, error) {
var err error
url := c.getURL(urlResourceManagedCertificateById)
url := c.getURL(urlResourceManagedCertificateByID)
url = fmt.Sprintf(url, managedCertId)
if c.user == nil || c.user.Company == nil {
err = fmt.Errorf("Must be autheticated")
return nil, err
}
request, err := http.NewRequest("GET", url, nil)
statusCode, _, body, err := c.request("GET", url, nil)
if err != nil {
return nil, err
}
request.Header.Add("tppl-api-key", c.apiKey)
request.Header.Add("accept", "application/json")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if c.verbose {
fmt.Printf("REQ: %s\n", url)
fmt.Printf("RES: %s\n", body)
}
switch resp.StatusCode {
switch statusCode {
case http.StatusOK:
var res = &managedCertificate{}
err = json.Unmarshal(body, res)
@@ -693,18 +556,75 @@ func (c *Connector) getManagedCertificate(managedCertId string) (*managedCertifi
if body != nil {
respErrors, err := parseResponseErrors(body)
if err == nil {
respError := fmt.Sprintf("Unexpected status code on Venafi Cloud certificate search. Status: %d\n", resp.StatusCode)
respError := fmt.Sprintf("Unexpected status code on Venafi Cloud certificate search. Status: %d\n", statusCode)
for _, e := range respErrors {
respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message)
}
return nil, fmt.Errorf(respError)
}
}
return nil, fmt.Errorf("Unexpected status code on Venafi Cloud certificate search. Status: %d", resp.StatusCode)
return nil, fmt.Errorf("Unexpected status code on Venafi Cloud certificate search. Status: %d", statusCode)
}
}
func (c *Connector) ImportCertificate(req *certificate.ImportRequest) (*certificate.ImportResponse, error) {
return nil, fmt.Errorf("import is not supported")
pBlock, _ := pem.Decode([]byte(req.CertificateData))
if pBlock == nil {
return nil, fmt.Errorf("can`t parse certificate")
}
zone := req.PolicyDN
if zone == "" {
zone = c.zone
}
base64.StdEncoding.EncodeToString(pBlock.Bytes)
fingerprint := certThumprint(pBlock.Bytes)
e := importRequestEndpoint{
OCSP: 1,
Certificates: []importRequestEndpointCert{
{
Certificate: base64.StdEncoding.EncodeToString(pBlock.Bytes),
Fingerprint: fingerprint,
},
},
Protocols: []importRequestEndpointProtocol{
{
Certificates: []string{fingerprint},
},
},
}
request := importRequest{
ZoneName: zone,
Endpoints: []importRequestEndpoint{e},
}
url := c.getURL(urlResourceDiscovery)
statusCode, status, body, err := c.request("POST", url, request)
if err != nil {
return nil, err
}
var r struct {
CreatedCertificates int
CreatedInstances int
UpdatedCertificates int
UpdatedInstances int
}
err = json.Unmarshal(body, &r)
if statusCode != http.StatusCreated {
return nil, fmt.Errorf("bad server status responce %d %s", statusCode, status)
} else if err != nil {
return nil, fmt.Errorf("can`t unmarshal json response %s", err)
} else if !(r.CreatedCertificates == 1 || r.UpdatedCertificates == 1) {
return nil, fmt.Errorf("certificate was not imported on unknown reason")
}
foundCert, err := c.searchCertificatesByFingerprint(fingerprint)
if err != nil {
return nil, err
}
if len(foundCert.Certificates) != 1 {
return nil, fmt.Errorf("certificate has been imported but could not be found on platform after that")
}
cert := foundCert.Certificates[0]
resp := &certificate.ImportResponse{CertificateDN: cert.SubjectCN[0], CertId: cert.Id}
return resp, nil
}
+8 -8
View File
@@ -50,13 +50,13 @@ type Paging struct {
const (
EQ Operator = "EQ"
FIND = "FIND"
GT = "GT"
GTE = "GTE"
IN = "IN"
LT = "LT"
LTE = "LTE"
MATCH = "MATCH"
FIND Operator = "FIND"
GT Operator = "GT"
GTE Operator = "GTE"
IN Operator = "IN"
LT Operator = "LT"
LTE Operator = "LTE"
MATCH Operator = "MATCH"
)
type CertificateSearchResponse struct {
@@ -69,7 +69,7 @@ type Certificate struct {
ManagedCertificateId string `json:"managedCertificateId"`
CertificateRequestId string `json:"certificateRequestId"`
SubjectCN []string `json:"subjectCN"`
/*...and many more fields... */
/* ... and many more fields ... */
}
func ParseCertificateSearchResponse(httpStatusCode int, body []byte) (searchResult *CertificateSearchResponse, err error) {
+46 -25
View File
@@ -17,17 +17,19 @@
package fake
import (
"crypto"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
"math/big"
"strings"
"time"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
)
type Connector struct {
@@ -44,17 +46,12 @@ func (c *Connector) GetType() endpoint.ConnectorType {
}
func (c *Connector) SetZone(z string) {
return
}
func (c *Connector) Ping() (err error) {
return
}
func (c *Connector) Register(email string) (err error) {
return
}
func (c *Connector) Authenticate(auth *endpoint.Authentication) (err error) {
return
}
@@ -71,8 +68,7 @@ func validateRequest(req *certificate.Request) error {
return nil
}
func (c *Connector) RequestCertificate(req *certificate.Request, zone string) (requestID string, err error) {
func (c *Connector) RequestCertificate(req *certificate.Request) (requestID string, err error) {
err = validateRequest(req)
if err != nil {
return "", fmt.Errorf("certificate request validation fail: %s", err)
@@ -83,7 +79,7 @@ func (c *Connector) RequestCertificate(req *certificate.Request, zone string) (r
switch req.CsrOrigin {
case certificate.LocalGeneratedCSR, certificate.UserProvidedCSR:
// should return CSR as requestID payload
fakeRequest.CSR = base64.StdEncoding.EncodeToString(req.CSR)
fakeRequest.CSR = base64.StdEncoding.EncodeToString(req.GetCSR())
case certificate.ServiceGeneratedCSR:
// should return certificate.Request as requestID payload
@@ -114,7 +110,7 @@ func issueCertificate(csr *x509.CertificateRequest) ([]byte, error) {
nameSet[name] = true
}
uniqNames := []string{}
for name, _ := range nameSet {
for name := range nameSet {
uniqNames = append(uniqNames, name)
}
csr.DNSNames = uniqNames
@@ -159,33 +155,29 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (pcc *certific
}
var csrPEMbytes []byte
var pk interface{}
var pk crypto.Signer
if fakeRequest.CSR != "" {
csrPEMbytes, err = base64.StdEncoding.DecodeString(fakeRequest.CSR)
if err != nil {
return nil, err
}
} else {
req := fakeRequest.Req
switch req.KeyType {
case certificate.KeyTypeECDSA:
req.PrivateKey, err = certificate.GenerateECDSAPrivateKey(req.KeyCurve)
case certificate.KeyTypeRSA:
req.PrivateKey, err = certificate.GenerateRSAPrivateKey(req.KeyLength)
default:
return nil, fmt.Errorf("Unable to generate certificate request, key type %s is not supported", req.KeyType.String())
}
err = req.GeneratePrivateKey()
if err != nil {
return
}
req.DNSNames = append(req.DNSNames, "fake-service-generated."+req.Subject.CommonName)
err = certificate.GenerateRequest(req, req.PrivateKey)
err = req.GenerateCSR()
if err != nil {
return
}
csrPEMbytes = pem.EncodeToMemory(certificate.GetCertificateRequestPEMBlock(req.CSR))
csrPEMbytes = req.GetCSR()
pk = req.PrivateKey
}
@@ -216,11 +208,17 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (pcc *certific
certBytes = append(cert_pem, []byte(caCertPEM)...)
}
pcc, err = certificate.PEMCollectionFromBytes(certBytes, req.ChainOption)
if err != nil {
return nil, err
}
// no key password -- no key
if pk != nil && req.KeyPassword != "" {
pcc.AddPrivateKey(pk, []byte(req.KeyPassword))
err = pcc.AddPrivateKey(pk, []byte(req.KeyPassword))
if err != nil {
return
}
}
err = req.CheckCertificate(pcc.Certificate)
return
}
@@ -229,7 +227,7 @@ func (c *Connector) RevokeCertificate(revReq *certificate.RevocationRequest) (er
return fmt.Errorf("revocation is not supported in -test-mode")
}
func (c *Connector) ReadZoneConfiguration(zone string) (config *endpoint.ZoneConfiguration, err error) {
func (c *Connector) ReadZoneConfiguration() (config *endpoint.ZoneConfiguration, err error) {
return endpoint.NewZoneConfiguration(), nil
}
@@ -241,3 +239,26 @@ func (c *Connector) RenewCertificate(revReq *certificate.RenewalRequest) (reques
func (c *Connector) ImportCertificate(req *certificate.ImportRequest) (*certificate.ImportResponse, error) {
return nil, fmt.Errorf("import is not supported in -test-mode")
}
func (c *Connector) ReadPolicyConfiguration() (policy *endpoint.Policy, err error) {
policy = &endpoint.Policy{
[]string{".*"},
[]string{".*"},
[]string{".*"},
[]string{".*"},
[]string{".*"},
[]string{".*"},
[]endpoint.AllowedKeyConfiguration{
{certificate.KeyTypeRSA, certificate.AllSupportedKeySizes(), nil},
{certificate.KeyTypeECDSA, nil, certificate.AllSupportedCurves()},
},
[]string{".*"},
[]string{".*"},
[]string{".*"},
[]string{".*"},
[]string{".*"},
true,
true,
}
return
}
+4 -20
View File
@@ -17,48 +17,32 @@
package fake
import (
"encoding/pem"
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
)
func (c *Connector) SetBaseURL(url string) error {
return nil
}
//GenerateRequest creates a new certificate request, based on the zone/policy configuration and the user data
func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *certificate.Request) (err error) {
switch req.CsrOrigin {
case certificate.LocalGeneratedCSR:
switch req.KeyType {
case certificate.KeyTypeECDSA:
req.PrivateKey, err = certificate.GenerateECDSAPrivateKey(req.KeyCurve)
case certificate.KeyTypeRSA:
if req.KeyLength == 0 {
req.KeyLength = 2048
}
req.PrivateKey, err = certificate.GenerateRSAPrivateKey(req.KeyLength)
default:
return fmt.Errorf("Unable to generate certificate request, key type %s is not supported", req.KeyType.String())
}
err = req.GeneratePrivateKey()
if err != nil {
return err
}
err = certificate.GenerateRequest(req, req.PrivateKey)
err = req.GenerateCSR()
if err != nil {
return err
}
req.CSR = pem.EncodeToMemory(certificate.GetCertificateRequestPEMBlock(req.CSR))
case certificate.UserProvidedCSR:
if req.CSR == nil {
if req.GetCSR() == nil {
return fmt.Errorf("CSR was supposed to be provided by user, but it's empty")
}
case certificate.ServiceGeneratedCSR:
req.CSR = nil
return nil
default:
return fmt.Errorf("Unexpected option in PrivateKeyOrigin")
+120 -259
View File
@@ -17,18 +17,16 @@
package tpp
import (
"bytes"
"crypto/x509"
"encoding/json"
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
"io/ioutil"
"log"
"net/http"
"strconv"
"regexp"
"strings"
"time"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
)
// Connector contains the base data needed to communicate with a TPP Server
@@ -41,9 +39,38 @@ type Connector struct {
}
// NewConnector creates a new TPP Connector object used to communicate with TPP
func NewConnector(verbose bool, trust *x509.CertPool) *Connector {
c := Connector{trust: trust, verbose: verbose}
return &c
func NewConnector(url string, zone string, verbose bool, trust *x509.CertPool) (*Connector, error) {
c := Connector{verbose: verbose, trust: trust, zone: zone}
var err error
c.baseURL, err = normalizeURL(url)
if err != nil {
return nil, err
}
return &c, nil
}
// normalizeURL normalizes the base URL used to communicate with TPP
func normalizeURL(url string) (normalizedURL string, err error) {
var baseUrlRegex = regexp.MustCompile(`^https://[a-z\d]+[-a-z\d.]+[a-z\d][:\d]*/vedsdk/$`)
modified := strings.ToLower(url)
if strings.HasPrefix(modified, "http://") {
modified = "https://" + modified[7:]
} else if !strings.HasPrefix(modified, "https://") {
modified = "https://" + modified
}
if !strings.HasSuffix(modified, "/") {
modified = modified + "/"
}
if !strings.HasSuffix(modified, "vedsdk/") {
modified += "vedsdk/"
}
if loc := baseUrlRegex.FindStringIndex(modified); loc == nil {
return "", fmt.Errorf("The specified TPP URL is invalid. %s\nExpected TPP URL format 'https://tpp.company.com/vedsdk/'", url)
}
normalizedURL = modified
return normalizedURL, nil
}
func (c *Connector) SetZone(z string) {
@@ -56,28 +83,14 @@ func (c *Connector) GetType() endpoint.ConnectorType {
//Ping attempts to connect to the TPP Server WebSDK API and returns an errror if it cannot
func (c *Connector) Ping() (err error) {
url, err := c.getURL("")
statusCode, status, _, err := c.request("GET", "", nil)
if err != nil {
return err
return
}
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("content-type", "application/json")
req.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(req)
if err != nil {
return err
} else if res.StatusCode != http.StatusOK {
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
err = fmt.Errorf("%s", string(body))
if statusCode != http.StatusOK {
err = fmt.Errorf(status)
}
return err
}
//Register does nothing for TPP
func (c *Connector) Register(email string) (err error) {
return nil
return
}
// Authenticate authenticates the user to the TPP
@@ -85,33 +98,17 @@ func (c *Connector) Authenticate(auth *endpoint.Authentication) (err error) {
if auth == nil {
return fmt.Errorf("failed to authenticate: missing credentials")
}
url, err := c.getURL(urlResourceAuthorize)
statusCode, status, body, err := c.request("POST", urlResourceAuthorize, authorizeResquest{Username: auth.User, Password: auth.Password})
if err != nil {
return err
return
}
b, _ := json.Marshal(authorizeResquest{Username: auth.User, Password: auth.Password})
payload := bytes.NewReader(b)
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("content-type", "application/json")
req.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(req)
if err == nil {
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
key, err := parseAuthorizeResult(res.StatusCode, res.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", urlResourceAuthorize, strings.Replace(fmt.Sprintf("%s", b), auth.Password, "********", -1))
}
return err
}
c.apiKey = key
return nil
key, err := parseAuthorizeResult(statusCode, status, body)
if err != nil {
return
}
return err
c.apiKey = key
return
}
func wrapAltNames(req *certificate.Request) (items []sanItem) {
@@ -127,6 +124,7 @@ func wrapAltNames(req *certificate.Request) (items []sanItem) {
return items
}
//todo:remove unused
func wrapKeyType(kt certificate.KeyType) string {
switch kt {
case certificate.KeyTypeRSA:
@@ -143,13 +141,15 @@ func prepareRequest(req *certificate.Request, zone string) (tppReq certificateRe
case certificate.LocalGeneratedCSR, certificate.UserProvidedCSR:
tppReq = certificateRequest{
PolicyDN: getPolicyDN(zone),
PKCS10: string(req.CSR),
CADN: req.CADN,
PKCS10: string(req.GetCSR()),
ObjectName: req.FriendlyName,
DisableAutomaticRenewal: true}
case certificate.ServiceGeneratedCSR:
tppReq = certificateRequest{
PolicyDN: getPolicyDN(zone),
CADN: req.CADN,
ObjectName: req.FriendlyName,
Subject: req.Subject.CommonName, // TODO: there is some problem because Subject is not only CN
SubjectAltNames: wrapAltNames(req),
@@ -172,43 +172,19 @@ func prepareRequest(req *certificate.Request, zone string) (tppReq certificateRe
}
// RequestCertificate submits the CSR to TPP returning the DN of the requested Certificate
func (c *Connector) RequestCertificate(req *certificate.Request, zone string) (requestID string, err error) {
func (c *Connector) RequestCertificate(req *certificate.Request) (requestID string, err error) {
if zone == "" {
zone = c.zone
}
tppCertificateRequest, err := prepareRequest(req, zone)
tppCertificateRequest, err := prepareRequest(req, c.zone)
if err != nil {
return "", err
}
b, _ := json.Marshal(tppCertificateRequest)
url, err := c.getURL(urlResourceCertificateRequest)
statusCode, status, body, err := c.request("POST", urlResourceCertificateRequest, tppCertificateRequest)
if err != nil {
return "", err
}
payload := bytes.NewReader(b)
request, _ := http.NewRequest("POST", url, payload)
request.Header.Add("x-venafi-api-key", c.apiKey)
request.Header.Add("content-type", "application/json")
request.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(request)
requestID, err = parseRequestResult(statusCode, status, body)
if err != nil {
return "", err
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
requestID, err = parseRequestResult(res.StatusCode, res.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", urlResourceCertificateRequest, b)
}
return "", fmt.Errorf("%s: %s", err, string(body))
return "", fmt.Errorf("%s: %s", err, string(body)) //todo: remove body from error
}
req.PickupID = requestID
return requestID, nil
@@ -248,12 +224,18 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates
startTime := time.Now()
for {
retrieveResponse, err := c.retrieveCertificateOnce(certReq)
var retrieveResponse *certificateRetrieveResponse
retrieveResponse, err = c.retrieveCertificateOnce(certReq)
if err != nil {
return nil, fmt.Errorf("unable to retrieve: %s", err)
}
if retrieveResponse.CertificateData != "" {
return newPEMCollectionFromResponse(retrieveResponse.CertificateData, req.ChainOption)
certificates, err = newPEMCollectionFromResponse(retrieveResponse.CertificateData, req.ChainOption)
if err != nil {
return
}
err = req.CheckCertificate(certificates.Certificate)
return
}
if req.Timeout == 0 {
return nil, endpoint.ErrCertificatePending{CertificateID: req.PickupID, Status: retrieveResponse.Status}
@@ -266,34 +248,14 @@ func (c *Connector) RetrieveCertificate(req *certificate.Request) (certificates
}
func (c *Connector) retrieveCertificateOnce(certReq certificateRetrieveRequest) (*certificateRetrieveResponse, error) {
url, err := c.getURL(urlResourceCertificateRetrieve)
statusCode, status, body, err := c.request("POST", urlResourceCertificateRetrieve, certReq)
if err != nil {
return nil, err
}
b, _ := json.Marshal(certReq)
payload := bytes.NewReader(b)
r, _ := http.NewRequest("POST", url, payload)
r.Header.Add("x-venafi-api-key", c.apiKey)
r.Header.Add("content-type", "application/json")
r.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(r)
retrieveResponse, err := parseRetrieveResult(statusCode, status, body)
if err != nil {
return nil, err
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
retrieveResponse, err := parseRetrieveResult(res.StatusCode, res.Status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", urlResourceCertificateRetrieve, b)
}
return nil, err
}
return &retrieveResponse, nil
}
@@ -319,38 +281,18 @@ func (c *Connector) RenewCertificate(renewReq *certificate.RenewalRequest) (requ
return "", fmt.Errorf("failed to create renewal request: CertificateDN or Thumbprint required")
}
url, err := c.getURL(urlResourceCertificateRenew)
if err != nil {
return "", err
}
var r = certificateRenewRequest{}
r.CertificateDN = renewReq.CertificateDN
if renewReq.CertificateRequest != nil && len(renewReq.CertificateRequest.CSR) > 0 {
r.PKCS10 = string(renewReq.CertificateRequest.CSR)
if renewReq.CertificateRequest != nil && len(renewReq.CertificateRequest.GetCSR()) != 0 {
r.PKCS10 = string(renewReq.CertificateRequest.GetCSR())
}
b, _ := json.Marshal(r)
payload := bytes.NewReader(b)
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-venafi-api-key", c.apiKey)
req.Header.Add("content-type", "application/json")
req.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(req)
statusCode, status, body, err := c.request("POST", urlResourceCertificateRenew, r)
if err != nil {
return "", err
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
response, err := parseRenewResult(res.StatusCode, res.Status, body)
response, err := parseRenewResult(statusCode, status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", url, b)
log.Printf("Response: %s", string(body))
}
return "", err
}
if !response.Success {
@@ -361,11 +303,6 @@ func (c *Connector) RenewCertificate(renewReq *certificate.RenewalRequest) (requ
// RevokeCertificate attempts to revoke the certificate
func (c *Connector) RevokeCertificate(revReq *certificate.RevocationRequest) (err error) {
url, err := c.getURL(urlResourceCertificateRevoke)
if err != nil {
return err
}
reason, ok := RevocationReasonsMap[revReq.Reason]
if !ok {
return fmt.Errorf("could not parse revocation reason `%s`", revReq.Reason)
@@ -378,28 +315,13 @@ func (c *Connector) RevokeCertificate(revReq *certificate.RevocationRequest) (er
revReq.Comments,
revReq.Disable,
}
b, _ := json.Marshal(r)
payload := bytes.NewReader(b)
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-venafi-api-key", c.apiKey)
req.Header.Add("content-type", "application/json")
req.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(req)
statusCode, status, body, err := c.request("POST", urlResourceCertificateRevoke, r)
if err != nil {
return err
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
revokeResponse, err := parseRevokeResult(res.StatusCode, res.Status, body)
revokeResponse, err := parseRevokeResult(statusCode, status, body)
if err != nil {
if c.verbose {
log.Printf("JSON sent for %s\n%s", urlResourceCertificateRevoke, b)
}
return err
return
}
if !revokeResponse.Success {
return fmt.Errorf("Revocation error: %s", revokeResponse.Error)
@@ -407,128 +329,67 @@ func (c *Connector) RevokeCertificate(revReq *certificate.RevocationRequest) (er
return
}
//ReadZoneConfiguration reads the policy data from TPP to get locked and pre-configured values for certificate requests
func (c *Connector) ReadZoneConfiguration(zone string) (config *endpoint.ZoneConfiguration, err error) {
zoneConfig := endpoint.NewZoneConfiguration()
zoneConfig.HashAlgorithm = x509.SHA256WithRSA
policyDN := getPolicyDN(zone)
keyType := certificate.KeyTypeRSA
func (c *Connector) ReadPolicyConfiguration() (policy *endpoint.Policy, err error) {
if c.zone == "" {
return nil, fmt.Errorf("empty zone")
}
rq := struct{ PolicyDN string }{getPolicyDN(c.zone)}
statusCode, status, body, err := c.request("POST", urlResourceCertificatePolicy, rq)
if err != nil {
return
}
var r struct {
Policy serverPolicy
}
if statusCode == http.StatusOK {
err = json.Unmarshal(body, &r)
p := r.Policy.toPolicy()
policy = &p
} else {
return nil, fmt.Errorf("Invalid status: %s Server data: %s", status, body)
}
return
}
url, err := c.getURL(urlResourceFindPolicy)
//ReadZoneConfiguration reads the policy data from TPP to get locked and pre-configured values for certificate requests
func (c *Connector) ReadZoneConfiguration() (config *endpoint.ZoneConfiguration, err error) {
if c.zone == "" {
return nil, fmt.Errorf("empty zone")
}
zoneConfig := endpoint.NewZoneConfiguration()
zoneConfig.HashAlgorithm = x509.SHA256WithRSA //todo: check this can have problem with ECDSA key
rq := struct{ PolicyDN string }{getPolicyDN(c.zone)}
statusCode, status, body, err := c.request("POST", urlResourceCertificatePolicy, rq)
if err != nil {
return
}
var r struct {
Policy serverPolicy
}
if statusCode != http.StatusOK {
return nil, fmt.Errorf("Invalid status: %s Server response: %s", status, string(body))
}
err = json.Unmarshal(body, &r)
if err != nil {
return nil, err
}
attributes := []string{tppAttributeOrg, tppAttributeOrgUnit, tppAttributeCountry, tppAttributeState, tppAttributeLocality, tppAttributeKeyAlgorithm, tppAttributeKeySize, tppAttributeEllipticCurve, tppAttributeRequestHash, tppAttributeManagementType, tppAttributeManualCSR}
for _, attrib := range attributes {
b, _ := json.Marshal(policyRequest{ObjectDN: policyDN, Class: "X509 Certificate", AttributeName: attrib})
payload := bytes.NewReader(b)
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-venafi-api-key", c.apiKey)
req.Header.Add("content-type", "application/json")
req.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(req)
if err == nil {
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
tppData, err := parseConfigResult(res.StatusCode, res.Status, body)
if tppData.Error == "" && (err != nil || tppData.Values == nil || len(tppData.Values) == 0) {
continue
} else if tppData.Error != "" && tppData.Result == 400 { //object does not exist
return nil, fmt.Errorf(tppData.Error)
}
switch attrib {
case tppAttributeOrg:
zoneConfig.Organization = tppData.Values[0]
zoneConfig.OrganizationLocked = tppData.Locked
case tppAttributeOrgUnit:
zoneConfig.OrganizationalUnit = tppData.Values
case tppAttributeCountry:
zoneConfig.Country = tppData.Values[0]
zoneConfig.CountryLocked = tppData.Locked
case tppAttributeState:
zoneConfig.Province = tppData.Values[0]
zoneConfig.ProvinceLocked = tppData.Locked
case tppAttributeLocality:
zoneConfig.Locality = tppData.Values[0]
zoneConfig.LocalityLocked = tppData.Locked
case tppAttributeKeyAlgorithm:
err = keyType.Set(tppData.Values[0])
if err == nil {
zoneConfig.AllowedKeyConfigurations = []endpoint.AllowedKeyConfiguration{endpoint.AllowedKeyConfiguration{KeyType: keyType}}
}
case tppAttributeKeySize:
temp, err := strconv.Atoi(tppData.Values[0])
if err == nil {
zoneConfig.AllowedKeyConfigurations = []endpoint.AllowedKeyConfiguration{endpoint.AllowedKeyConfiguration{KeyType: keyType, KeySizes: []int{temp}}}
zoneConfig.KeySizeLocked = tppData.Locked
}
case tppAttributeEllipticCurve:
curve := certificate.EllipticCurveP256
err = curve.Set(tppData.Values[0])
if err == nil {
zoneConfig.AllowedKeyConfigurations = []endpoint.AllowedKeyConfiguration{endpoint.AllowedKeyConfiguration{KeyType: certificate.KeyTypeECDSA, KeyCurves: []certificate.EllipticCurve{curve}}}
zoneConfig.KeySizeLocked = tppData.Locked
}
case tppAttributeRequestHash:
alg, err := strconv.Atoi(tppData.Values[0])
if err == nil {
switch alg {
case pkcs10HashAlgorithmSha1:
zoneConfig.HashAlgorithm = x509.SHA1WithRSA
case pkcs10HashAlgorithmSha384:
zoneConfig.HashAlgorithm = x509.SHA384WithRSA
case pkcs10HashAlgorithmSha512:
zoneConfig.HashAlgorithm = x509.SHA512WithRSA
default:
zoneConfig.HashAlgorithm = x509.SHA256WithRSA
}
}
case tppAttributeManagementType, tppAttributeManualCSR:
if tppData.Locked {
zoneConfig.CustomAttributeValues[attrib] = tppData.Values[0]
}
}
} else {
if c.verbose {
log.Printf("JSON sent for %s\n%s", urlResourceFindPolicy, b)
}
return nil, err
}
}
p := r.Policy.toPolicy()
r.Policy.toZoneConfig(zoneConfig)
zoneConfig.Policy = p
return zoneConfig, nil
}
func (c *Connector) ImportCertificate(r *certificate.ImportRequest) (*certificate.ImportResponse, error) {
url, err := c.getURL(urlResourceCertificateImport)
if err != nil {
return nil, err
}
if r.PolicyDN == "" {
r.PolicyDN = getPolicyDN(c.zone)
}
b, _ := json.Marshal(r)
payload := bytes.NewReader(b)
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-venafi-api-key", c.apiKey)
req.Header.Add("content-type", "application/json")
req.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(req)
statusCode, _, body, err := c.request("POST", urlResourceCertificateImport, r)
if err != nil {
return nil, err
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
switch res.StatusCode {
switch statusCode {
case http.StatusOK:
var response = &certificate.ImportResponse{}
@@ -546,6 +407,6 @@ func (c *Connector) ImportCertificate(r *certificate.ImportRequest) (*certificat
}
return nil, fmt.Errorf("%s", errorResponse.Error)
default:
return nil, fmt.Errorf("unexpected response status %d: %s", res.StatusCode, string(b))
return nil, fmt.Errorf("unexpected response status %d: %s", statusCode, string(body))
}
}
+3 -25
View File
@@ -19,7 +19,6 @@ package tpp
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
@@ -53,33 +52,12 @@ func (c *Connector) searchCertificates(req *SearchRequest) (*CertificateSearchRe
var err error
url, _ := c.getURL(urlResourceCertificateSearch)
url = fmt.Sprintf("%s?%s", url, strings.Join(*req, "&"))
request, err := http.NewRequest("GET", url, nil)
url := fmt.Sprintf("%s?%s", urlResourceCertificateSearch, strings.Join(*req, "&"))
statusCode, _, body, err := c.request("GET", urlResource(url), nil)
if err != nil {
return nil, err
}
request.Header.Add("x-venafi-api-key", c.apiKey)
request.Header.Add("cache-control", "no-cache")
request.Header.Add("accept", "application/json")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if c.verbose {
fmt.Printf("REQ: %s\n", url)
fmt.Printf("RES: %s\n", body)
}
searchResult, err := ParseCertificateSearchResponse(resp.StatusCode, body)
searchResult, err := ParseCertificateSearchResponse(statusCode, body)
if err != nil {
return nil, err
}
+282 -103
View File
@@ -17,17 +17,21 @@
package tpp
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
"io"
"io/ioutil"
"log"
"net/http"
"regexp"
"strings"
"github.com/Venafi/vcert/pkg/certificate"
"github.com/Venafi/vcert/pkg/endpoint"
)
const defaultKeySize = 2048
@@ -73,7 +77,7 @@ type certificateRetrieveResponse struct {
type RevocationReason int
// this maps *certificate.RevocationRequest.Reason to TPP-specific webSDK codes
// RevocationReasonsMap maps *certificate.RevocationRequest.Reason to TPP-specific webSDK codes
var RevocationReasonsMap = map[string]RevocationReason{
"": 0, // NoReason
"none": 0, //
@@ -145,13 +149,14 @@ type urlResource string
const (
urlResourceAuthorize urlResource = "authorize/"
urlResourceCertificateRequest = "certificates/request"
urlResourceCertificateRetrieve = "certificates/retrieve"
urlResourceFindPolicy = "config/findpolicy"
urlResourceCertificateRevoke = "certificates/revoke"
urlResourceCertificateRenew = "certificates/renew"
urlResourceCertificateSearch = "certificates/"
urlResourceCertificateImport = "certificates/import"
urlResourceCertificateRequest urlResource = "certificates/request"
urlResourceCertificateRetrieve urlResource = "certificates/retrieve"
urlResourceFindPolicy urlResource = "config/findpolicy"
urlResourceCertificateRevoke urlResource = "certificates/revoke"
urlResourceCertificateRenew urlResource = "certificates/renew"
urlResourceCertificateSearch urlResource = "certificates/"
urlResourceCertificateImport urlResource = "certificates/import"
urlResourceCertificatePolicy urlResource = "certificates/checkpolicy"
)
const (
@@ -201,36 +206,6 @@ func retrieveChainOptionFromString(order string) retrieveChainOption {
}
}
// SetBaseURL sets the base URL used to cummuncate with TPP
func (c *Connector) SetBaseURL(url string) error {
modified := strings.ToLower(url)
reg := regexp.MustCompile("^http(|s)://")
if reg.FindStringIndex(modified) == nil {
modified = "https://" + modified
} else {
modified = reg.ReplaceAllString(modified, "https://")
}
reg = regexp.MustCompile("^https://.+?/")
if reg.FindStringIndex(modified) == nil {
modified = modified + "/"
}
reg = regexp.MustCompile("/vedsdk(|/)$")
if reg.FindStringIndex(modified) == nil {
modified += "vedsdk/"
} else {
modified = reg.ReplaceAllString(modified, "/vedsdk/")
}
reg = regexp.MustCompile("^https://[a-z\\d]+[-a-z\\d.]+[a-z\\d][:\\d]*/vedsdk/$")
if loc := reg.FindStringIndex(modified); loc == nil {
return fmt.Errorf("The specified TPP URL is invalid. %s\nExpected TPP URL format 'https://tpp.company.com/vedsdk/'", url)
}
c.baseURL = modified
return nil
}
func (c *Connector) getURL(resource urlResource) (string, error) {
if c.baseURL == "" {
return "", fmt.Errorf("The Host URL has not been set")
@@ -238,19 +213,70 @@ func (c *Connector) getURL(resource urlResource) (string, error) {
return fmt.Sprintf("%s%s", c.baseURL, resource), nil
}
func (c *Connector) request(method string, resource urlResource, data interface{}) (statusCode int, statusText string, body []byte, err error) {
url, err := c.getURL(resource)
if err != nil {
return
}
var payload io.Reader
var b []byte
if method == "POST" {
b, _ = json.Marshal(data)
payload = bytes.NewReader(b)
}
r, _ := http.NewRequest(method, url, payload)
if c.apiKey != "" {
r.Header.Add("x-venafi-api-key", c.apiKey)
}
r.Header.Add("content-type", "application/json")
r.Header.Add("cache-control", "no-cache")
res, err := c.getHTTPClient().Do(r)
if res != nil {
statusCode = res.StatusCode
statusText = res.Status
}
if err != nil {
return
}
defer res.Body.Close()
body, err = ioutil.ReadAll(res.Body)
// Do not enable trace in production
trace := false // IMPORTANT: sensitive information can be diclosured
// I hope you know what are you doing
if trace {
log.Println("#################")
if method == "POST" {
log.Printf("JSON sent for %s\n%s\n", url, string(b))
} else {
log.Printf("%s request sent to %s\n", method, url)
}
log.Printf("Response:\n%s\n", string(body))
} else if c.verbose {
log.Printf("Got %s status for %s %s\n", statusText, method, url)
}
return
}
func (c *Connector) getHTTPClient() *http.Client {
if c.trust != nil {
tr := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: c.trust}}
return &http.Client{Transport: tr}
tlsConfig := http.DefaultTransport.(*http.Transport).TLSClientConfig
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
tlsConfig.RootCAs = c.trust
return &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
}
return http.DefaultClient
}
//GenerateRequest creates a new certificate request, based on the zone/policy configuration and the user data
// GenerateRequest creates a new certificate request, based on the zone/policy configuration and the user data
func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *certificate.Request) (err error) {
if config == nil {
config, err = c.ReadZoneConfiguration(c.zone)
config, err = c.ReadZoneConfiguration()
if err != nil {
return fmt.Errorf("could not read zone configuration: %s", err)
}
@@ -268,42 +294,32 @@ func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *cer
if config.CustomAttributeValues[tppAttributeManualCSR] == "0" {
return fmt.Errorf("Unable to request certificate by local generated CSR when zone configuration is 'Manual Csr' = 0")
}
switch req.KeyType {
case certificate.KeyTypeECDSA:
req.PrivateKey, err = certificate.GenerateECDSAPrivateKey(req.KeyCurve)
case certificate.KeyTypeRSA:
req.PrivateKey, err = certificate.GenerateRSAPrivateKey(req.KeyLength)
default:
return fmt.Errorf("Unable to generate certificate request, key type %s is not supported", req.KeyType.String())
}
err = req.GeneratePrivateKey()
if err != nil {
return err
}
err = certificate.GenerateRequest(req, req.PrivateKey)
err = req.GenerateCSR()
if err != nil {
return err
}
req.CSR = pem.EncodeToMemory(certificate.GetCertificateRequestPEMBlock(req.CSR))
case certificate.UserProvidedCSR:
if config.CustomAttributeValues[tppAttributeManualCSR] == "0" {
return fmt.Errorf("Unable to request certificate with user provided CSR when zone configuration is 'Manual Csr' = 0")
}
if req.CSR == nil || len(req.CSR) == 0 {
if len(req.GetCSR()) == 0 {
return fmt.Errorf("CSR was supposed to be provided by user, but it's empty")
}
case certificate.ServiceGeneratedCSR:
req.CSR = nil
}
return nil
}
func getPolicyDN(zone string) string {
modified := zone
reg := regexp.MustCompile("^\\\\VED\\\\Policy")
reg := regexp.MustCompile(`^\\VED\\Policy`)
if reg.FindStringIndex(modified) == nil {
reg = regexp.MustCompile("^\\\\")
reg = regexp.MustCompile(`^\\`)
if reg.FindStringIndex(modified) == nil {
modified = "\\" + modified
}
@@ -325,14 +341,9 @@ func parseAuthorizeResult(httpStatusCode int, httpStatus string, body []byte) (s
}
}
func parseAuthorizeData(b []byte) (authorizeResponse, error) {
var data authorizeResponse
err := json.Unmarshal(b, &data)
if err != nil {
return data, err
}
return data, nil
func parseAuthorizeData(b []byte) (data authorizeResponse, err error) {
err = json.Unmarshal(b, &data)
return
}
func parseConfigResult(httpStatusCode int, httpStatus string, body []byte) (tppData tppPolicyData, err error) {
@@ -349,14 +360,9 @@ func parseConfigResult(httpStatusCode int, httpStatus string, body []byte) (tppD
}
}
func parseConfigData(b []byte) (tppPolicyData, error) {
var data tppPolicyData
err := json.Unmarshal(b, &data)
if err != nil {
return data, err
}
return data, nil
func parseConfigData(b []byte) (data tppPolicyData, err error) {
err = json.Unmarshal(b, &data)
return
}
func parseRequestResult(httpStatusCode int, httpStatus string, body []byte) (string, error) {
@@ -372,14 +378,9 @@ func parseRequestResult(httpStatusCode int, httpStatus string, body []byte) (str
}
}
func parseRequestData(b []byte) (certificateRequestResponse, error) {
var data certificateRequestResponse
err := json.Unmarshal(b, &data)
if err != nil {
return data, err
}
return data, nil
func parseRequestData(b []byte) (data certificateRequestResponse, err error) {
err = json.Unmarshal(b, &data)
return
}
func parseRetrieveResult(httpStatusCode int, httpStatus string, body []byte) (certificateRetrieveResponse, error) {
@@ -396,14 +397,9 @@ func parseRetrieveResult(httpStatusCode int, httpStatus string, body []byte) (ce
}
}
func parseRetrieveData(b []byte) (certificateRetrieveResponse, error) {
var data certificateRetrieveResponse
err := json.Unmarshal(b, &data)
if err != nil {
return data, err
}
// fmt.Printf("\n\n%s\n\n%+v\n\n", string(b), data)
return data, nil
func parseRetrieveData(b []byte) (data certificateRetrieveResponse, err error) {
err = json.Unmarshal(b, &data)
return
}
func parseRevokeResult(httpStatusCode int, httpStatus string, body []byte) (certificateRevokeResponse, error) {
@@ -420,13 +416,9 @@ func parseRevokeResult(httpStatusCode int, httpStatus string, body []byte) (cert
}
}
func parseRevokeData(b []byte) (certificateRevokeResponse, error) {
var data certificateRevokeResponse
err := json.Unmarshal(b, &data)
if err != nil {
return data, err
}
return data, nil
func parseRevokeData(b []byte) (data certificateRevokeResponse, err error) {
err = json.Unmarshal(b, &data)
return
}
func parseRenewResult(httpStatusCode int, httpStatus string, body []byte) (resp certificateRenewResponse, err error) {
@@ -437,10 +429,9 @@ func parseRenewResult(httpStatusCode int, httpStatus string, body []byte) (resp
return resp, nil
}
func parseRenewData(b []byte) (certificateRenewResponse, error) {
var data certificateRenewResponse
err := json.Unmarshal(b, &data)
return data, err
func parseRenewData(b []byte) (data certificateRenewResponse, err error) {
err = json.Unmarshal(b, &data)
return
}
func newPEMCollectionFromResponse(base64Response string, chainOrder certificate.ChainOption) (*certificate.PEMCollection, error) {
@@ -454,3 +445,191 @@ func newPEMCollectionFromResponse(base64Response string, chainOrder certificate.
}
return nil, nil
}
type _strValue struct {
Locked bool
Value string
}
type serverPolicy struct {
CertificateAuthority _strValue
CsrGeneration _strValue
KeyGeneration _strValue
KeyPair struct {
KeyAlgorithm _strValue
KeySize struct {
Locked bool
Value int
}
EllipticCurve struct {
Locked bool
Value string
}
}
ManagementType _strValue
PrivateKeyReuseAllowed bool
SubjAltNameDnsAllowed bool
SubjAltNameEmailAllowed bool
SubjAltNameIpAllowed bool
SubjAltNameUpnAllowed bool
SubjAltNameUriAllowed bool
Subject struct {
City _strValue
Country _strValue
Organization _strValue
OrganizationalUnit struct {
Locked bool
Values []string
}
State _strValue
}
UniqueSubjectEnforced bool
WhitelistedDomains []string
WildcardsAllowed bool
}
func (sp serverPolicy) toZoneConfig(zc *endpoint.ZoneConfiguration) {
zc.Country = sp.Subject.Country.Value
zc.Organization = sp.Subject.Organization.Value
zc.OrganizationalUnit = sp.Subject.OrganizationalUnit.Values
zc.Province = sp.Subject.State.Value
zc.Locality = sp.Subject.City.Value
}
func (sp serverPolicy) toPolicy() (p endpoint.Policy) {
addStartEnd := func(s string) string {
if !strings.HasPrefix(s, "^") {
s = "^" + s
}
if !strings.HasSuffix(s, "$") {
s = s + "$"
}
return s
}
escapeOne := func(s string) string {
return addStartEnd(regexp.QuoteMeta(s))
}
escapeArray := func(l []string) []string {
escaped := make([]string, len(l))
for i, r := range l {
escaped[i] = escapeOne(r)
}
return escaped
}
const allAllowedRegex = ".*"
if len(sp.WhitelistedDomains) == 0 {
p.SubjectCNRegexes = []string{allAllowedRegex}
} else {
p.SubjectCNRegexes = make([]string, len(sp.WhitelistedDomains))
for i, d := range sp.WhitelistedDomains {
if sp.WildcardsAllowed {
p.SubjectCNRegexes[i] = addStartEnd(".*" + regexp.QuoteMeta("."+d))
} else {
p.SubjectCNRegexes[i] = escapeOne(d)
}
}
}
if sp.Subject.OrganizationalUnit.Locked {
p.SubjectOURegexes = escapeArray(sp.Subject.OrganizationalUnit.Values)
} else {
p.SubjectOURegexes = []string{allAllowedRegex}
}
if sp.Subject.Organization.Locked {
p.SubjectORegexes = []string{escapeOne(sp.Subject.Organization.Value)}
} else {
p.SubjectORegexes = []string{allAllowedRegex}
}
if sp.Subject.City.Locked {
p.SubjectLRegexes = []string{escapeOne(sp.Subject.City.Value)}
} else {
p.SubjectLRegexes = []string{allAllowedRegex}
}
if sp.Subject.State.Locked {
p.SubjectSTRegexes = []string{escapeOne(sp.Subject.State.Value)}
} else {
p.SubjectSTRegexes = []string{allAllowedRegex}
}
if sp.Subject.Country.Locked {
p.SubjectCRegexes = []string{escapeOne(sp.Subject.Country.Value)}
} else {
p.SubjectCRegexes = []string{allAllowedRegex}
}
if sp.SubjAltNameDnsAllowed {
if len(sp.WhitelistedDomains) == 0 {
p.DnsSanRegExs = []string{allAllowedRegex}
} else {
p.DnsSanRegExs = make([]string, len(sp.WhitelistedDomains))
for i, d := range sp.WhitelistedDomains {
if sp.WildcardsAllowed {
p.DnsSanRegExs[i] = addStartEnd(".*" + regexp.QuoteMeta("."+d))
} else {
p.DnsSanRegExs[i] = escapeOne(d)
}
}
}
} else {
p.DnsSanRegExs = []string{}
}
if sp.SubjAltNameIpAllowed {
p.IpSanRegExs = []string{allAllowedRegex}
} else {
p.IpSanRegExs = []string{}
}
if sp.SubjAltNameEmailAllowed {
p.EmailSanRegExs = []string{allAllowedRegex}
} else {
p.EmailSanRegExs = []string{}
}
if sp.SubjAltNameUriAllowed {
p.UriSanRegExs = []string{allAllowedRegex}
} else {
p.UriSanRegExs = []string{}
}
if sp.SubjAltNameUpnAllowed {
p.UpnSanRegExs = []string{allAllowedRegex}
} else {
p.UpnSanRegExs = []string{}
}
if sp.KeyPair.KeyAlgorithm.Locked {
var keyType certificate.KeyType
if err := keyType.Set(sp.KeyPair.KeyAlgorithm.Value); err != nil {
panic(err)
}
key := endpoint.AllowedKeyConfiguration{KeyType: keyType}
if keyType == certificate.KeyTypeRSA {
if sp.KeyPair.KeySize.Locked {
for _, i := range certificate.AllSupportedKeySizes() {
if i >= sp.KeyPair.KeySize.Value {
key.KeySizes = append(key.KeySizes, i)
}
}
} else {
key.KeySizes = certificate.AllSupportedKeySizes()
}
} else {
var curve certificate.EllipticCurve
if sp.KeyPair.EllipticCurve.Locked {
if err := curve.Set(sp.KeyPair.EllipticCurve.Value); err != nil {
panic(err)
}
key.KeyCurves = append(key.KeyCurves, curve)
} else {
key.KeyCurves = certificate.AllSupportedCurves()
}
}
p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, key)
} else {
p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, endpoint.AllowedKeyConfiguration{
KeyType: certificate.KeyTypeRSA, KeySizes: certificate.AllSupportedKeySizes(),
})
p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, endpoint.AllowedKeyConfiguration{
KeyType: certificate.KeyTypeECDSA, KeyCurves: certificate.AllSupportedCurves(),
})
}
p.AllowWildcards = sp.WildcardsAllowed
p.AllowKeyReuse = sp.PrivateKeyReuseAllowed
return
}
+8 -11
View File
@@ -13,19 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
VCert is a Go library, SDK, and command line utility designed to simplify key generation and enrollment of machine identities (also known as SSL/TLS certificates and keys) that comply with enterprise security policy by using the Venafi Platform or Venafi Cloud.
*/
package vcert
import (
"fmt"
)
//ProjectName contains the friendly name of the vcert utiltity
const ProjectName string = "Venafi Certificate Utility"
//projectName contains the friendly name of the vcert utiltity
const projectName string = "Venafi Certificate Utility"
var (
versionString string
versionBuildTimeStamp string
versionString string
)
//GetFormattedVersionString gets a friendly printable string to represent the version
@@ -33,13 +35,8 @@ func GetFormattedVersionString() string {
if versionBuildTimeStamp != "" {
versionBuildTimeStamp = fmt.Sprintf("\tBuild Timestamp: %s\n", versionBuildTimeStamp)
}
return fmt.Sprintf("%s\n\tVersion: %s\n%s", ProjectName, GetVersionString(), versionBuildTimeStamp)
}
//GetVersionString gets a simple version string
func GetVersionString() string {
if versionString == "" {
versionString = "3.18.3.1"
versionString = "Unknown"
}
return versionString
return fmt.Sprintf("%s\n\tVersion: %s\n%s", projectName, versionString, versionBuildTimeStamp)
}
+1 -1
View File
@@ -24,7 +24,7 @@ github.com/NYTimes/gziphandler
github.com/PuerkitoBio/purell
# github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
github.com/PuerkitoBio/urlesc
# github.com/Venafi/vcert v0.0.0-20181029235941-5068538d4d65
# github.com/Venafi/vcert v0.0.0-20190530133915-e207710a0ab9
github.com/Venafi/vcert
github.com/Venafi/vcert/pkg/certificate
github.com/Venafi/vcert/pkg/endpoint