You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ro...@apache.org on 2018/09/28 00:12:35 UTC

[trafficcontrol] branch master updated (ec9e0ef -> 11a57ad)

This is an automated email from the ASF dual-hosted git repository.

rob pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git.


    from ec9e0ef  fix for changed signature
     new eaffeb5  Finish up DS SSL keys endpoints in TO Go
     new 62c3c21  Update CHANGELOG.md
     new 26a30b2  Fix DS SSL API request validation, tenancy checks, versions
     new 11a57ad  Fix riak content-type json header, use JSONIntStr for the version

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.md                                       |   4 +
 lib/go-tc/deliveryservice_ssl_keys.go              |  95 ++++---
 lib/go-util/num.go                                 |   8 +
 .../deliveryservice/deliveryservicesv13.go         |   4 +-
 .../traffic_ops_golang/deliveryservice/keys.go     | 248 ++++++++++--------
 .../deliveryservice/keys_test.go                   | 284 ++++++++++-----------
 .../traffic_ops_golang/deliveryservice/sslkeys.go  |  33 ++-
 traffic_ops/traffic_ops_golang/riaksvc/dsutil.go   |  84 +-----
 traffic_ops/traffic_ops_golang/routes.go           |  11 +-
 9 files changed, 384 insertions(+), 387 deletions(-)


[trafficcontrol] 01/04: Finish up DS SSL keys endpoints in TO Go

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit eaffeb59a74b16ecc1e8c3af14102d48548c4b9d
Author: Rawlin Peters <ra...@comcast.com>
AuthorDate: Tue Sep 18 16:44:41 2018 -0600

    Finish up DS SSL keys endpoints in TO Go
    
    Finish out the '-wip' DS sslkeys endpoints and fix up what was already
    there.
---
 lib/go-tc/deliveryservice_ssl_keys.go              |   2 +-
 .../deliveryservice/deliveryservicesv13.go         |   4 +-
 .../traffic_ops_golang/deliveryservice/keys.go     | 220 ++++++++--------
 .../deliveryservice/keys_test.go                   | 284 ++++++++++-----------
 .../traffic_ops_golang/deliveryservice/sslkeys.go  |   4 +-
 traffic_ops/traffic_ops_golang/riaksvc/dsutil.go   |  78 +-----
 traffic_ops/traffic_ops_golang/routes.go           |  11 +-
 7 files changed, 267 insertions(+), 336 deletions(-)

diff --git a/lib/go-tc/deliveryservice_ssl_keys.go b/lib/go-tc/deliveryservice_ssl_keys.go
index 0bfb15c..bab764a 100644
--- a/lib/go-tc/deliveryservice_ssl_keys.go
+++ b/lib/go-tc/deliveryservice_ssl_keys.go
@@ -44,7 +44,7 @@ type DeliveryServiceSSLKeysCertificate struct {
 // DeliveryServiceSSLKeys ...
 type DeliveryServiceSSLKeys struct {
 	CDN             string                            `json:"cdn,omitempty"`
-	DeliveryService string                            `json:"DeliveryService,omitempty"`
+	DeliveryService string                            `json:"deliveryservice,omitempty"`
 	BusinessUnit    string                            `json:"businessUnit,omitempty"`
 	City            string                            `json:"city,omitempty"`
 	Organization    string                            `json:"organization,omitempty"`
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
index 649afb1..3e2a64f 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
@@ -652,7 +652,7 @@ func updateSSLKeys(ds *tc.DeliveryServiceNullable, hostName string, tx *sql.Tx,
 	if ds.XMLID == nil {
 		return errors.New("delivery services has no XMLID!")
 	}
-	key, ok, err := riaksvc.GetDeliveryServiceSSLKeysObjTx(*ds.XMLID, "latest", tx, cfg.RiakAuthOptions)
+	key, ok, err := riaksvc.GetDeliveryServiceSSLKeysObj(*ds.XMLID, riaksvc.DSSSLKeyVersionLatest, tx, cfg.RiakAuthOptions)
 	if err != nil {
 		return errors.New("getting SSL key: " + err.Error())
 	}
@@ -661,7 +661,7 @@ func updateSSLKeys(ds *tc.DeliveryServiceNullable, hostName string, tx *sql.Tx,
 	}
 	key.DeliveryService = *ds.XMLID
 	key.Hostname = hostName
-	if err := riaksvc.PutDeliveryServiceSSLKeysObjTx(key, tx, cfg.RiakAuthOptions); err != nil {
+	if err := riaksvc.PutDeliveryServiceSSLKeysObj(key, tx, cfg.RiakAuthOptions); err != nil {
 		return errors.New("putting updated SSL key: " + err.Error())
 	}
 	return nil
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
index aac39dc..830ec07 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
@@ -27,7 +27,6 @@ import (
 	"encoding/json"
 	"encoding/pem"
 	"errors"
-	"fmt"
 	"net/http"
 	"strings"
 
@@ -37,6 +36,10 @@ import (
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
 )
 
+const (
+	PemCertEndMarker = "-----END CERTIFICATE-----"
+)
+
 // AddSSLKeys adds the given ssl keys to the given delivery service.
 func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
 	inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
@@ -46,7 +49,7 @@ func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
 	}
 	defer inf.Close()
 	if !inf.Config.RiakEnabled {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("adding Riak SSL keys for delivery service:: riak is not configured"))
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("adding SSL keys to Riak for delivery service: Riak is not configured"))
 		return
 	}
 	keysObj := tc.DeliveryServiceSSLKeys{}
@@ -58,22 +61,28 @@ func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
-	certChain, err := verifyAndEncodeCertificate(keysObj.Certificate.Crt, "")
+	certChain, err := verifyCertificate(keysObj.Certificate.Crt, "")
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("verifying certificate: "+err.Error()), nil)
 		return
 	}
 	keysObj.Certificate.Crt = certChain
+	base64EncodeCertificate(&keysObj.Certificate)
+	if err := riaksvc.PutDeliveryServiceSSLKeysObj(keysObj, inf.Tx.Tx, inf.Config.RiakAuthOptions); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, nil, errors.New("putting SSL keys in Riak for delivery service '"+keysObj.DeliveryService+"': "+err.Error()))
+		return
+	}
+	keysObj.Version = riaksvc.DSSSLKeyVersionLatest
 	if err := riaksvc.PutDeliveryServiceSSLKeysObj(keysObj, inf.Tx.Tx, inf.Config.RiakAuthOptions); err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, nil, errors.New("putting Riak SSL keys for delivery service '"+keysObj.DeliveryService+"': "+err.Error()))
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, nil, errors.New("putting latest SSL keys in Riak for delivery service '"+keysObj.DeliveryService+"': "+err.Error()))
 		return
 	}
-	api.WriteRespRaw(w, r, keysObj)
+	api.WriteResp(w, r, "Successfully added ssl keys for "+keysObj.DeliveryService)
 }
 
 // GetSSLKeysByHostName fetches the ssl keys for a deliveryservice specified by the fully qualified hostname
 func GetSSLKeysByHostName(w http.ResponseWriter, r *http.Request) {
-	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"hostName"}, nil)
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"hostname"}, nil)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
@@ -81,12 +90,11 @@ func GetSSLKeysByHostName(w http.ResponseWriter, r *http.Request) {
 	defer inf.Close()
 
 	if inf.Config.RiakEnabled == false {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusServiceUnavailable, errors.New("The RIAK service is unavailable"), errors.New("getting Riak SSL keys by host name: riak is not configured"))
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusServiceUnavailable, errors.New("the Riak service is unavailable"), errors.New("getting SSL keys from Riak by host name: Riak is not configured"))
 		return
 	}
 
-	version := inf.Params["version"]
-	hostName := inf.Params["hostName"]
+	hostName := inf.Params["hostname"]
 	domainName := ""
 	hostRegex := ""
 	strArr := strings.Split(hostName, ".")
@@ -96,7 +104,7 @@ func GetSSLKeysByHostName(w http.ResponseWriter, r *http.Request) {
 			domainName += strArr[i] + "."
 		}
 		domainName += strArr[ln-1]
-		hostRegex = ".*\\." + strArr[1] + "\\..*"
+		hostRegex = `.*\.` + strArr[1] + `\..*`
 	}
 
 	// lookup the cdnID
@@ -120,36 +128,28 @@ func GetSSLKeysByHostName(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if userErr, sysErr, errCode := tenant.Check(inf.User, xmlID, inf.Tx.Tx); userErr != nil || sysErr != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
-		return
-	}
-	keyObj, ok, err := riaksvc.GetDeliveryServiceSSLKeysObj(xmlID, version, inf.Tx.Tx, inf.Config.RiakAuthOptions)
-	if err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("getting ssl keys: "+err.Error()))
-		return
-	}
-	if !ok {
-		api.WriteRespAlert(w, r, tc.InfoLevel, "no object found for the specified key")
-		return
-	}
-	api.WriteResp(w, r, keyObj)
+	getSSLKeysByXMLIDHelper(xmlID, inf, w, r)
 }
 
 // GetSSLKeysByXMLID fetches the deliveryservice ssl keys by the specified xmlID.
 func GetSSLKeysByXMLID(w http.ResponseWriter, r *http.Request) {
-	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"xmlID"}, nil)
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"xmlid"}, nil)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
 	defer inf.Close()
 	if inf.Config.RiakEnabled == false {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusServiceUnavailable, errors.New("The RIAK service is unavailable"), errors.New("getting Riak SSL keys by xml id: riak is not configured"))
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusServiceUnavailable, errors.New("the Riak service is unavailable"), errors.New("getting SSL keys from Riak by xml id: Riak is not configured"))
 		return
 	}
+	xmlID := inf.Params["xmlid"]
+	getSSLKeysByXMLIDHelper(xmlID, inf, w, r)
+}
+
+func getSSLKeysByXMLIDHelper(xmlID string, inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 	version := inf.Params["version"]
-	xmlID := inf.Params["xmlID"]
+	decode := inf.Params["decode"]
 	if userErr, sysErr, errCode := tenant.Check(inf.User, xmlID, inf.Tx.Tx); userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
@@ -160,29 +160,65 @@ func GetSSLKeysByXMLID(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	if !ok {
-		api.WriteRespAlert(w, r, tc.InfoLevel, "no object found for the specified key")
+		api.WriteRespAlertObj(w, r, tc.InfoLevel, "no object found for the specified key", struct{}{}) // empty response object because Perl
 		return
 	}
+	if decode != "" && decode != "0" { // the Perl version checked the decode string as: if ( $decode )
+		err = base64DecodeCertificate(&keyObj.Certificate)
+		if err != nil {
+			api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("getting SSL keys for XMLID '"+xmlID+"': "+err.Error()))
+			return
+		}
+	}
 	api.WriteResp(w, r, keyObj)
 }
 
+func base64DecodeCertificate(cert *tc.DeliveryServiceSSLKeysCertificate) error {
+	csrDec, err := base64.StdEncoding.DecodeString(cert.CSR)
+	if err != nil {
+		return errors.New("base64 decoding csr: " + err.Error())
+	}
+	cert.CSR = string(csrDec)
+	crtDec, err := base64.StdEncoding.DecodeString(cert.Crt)
+	if err != nil {
+		return errors.New("base64 decoding crt: " + err.Error())
+	}
+	cert.Crt = string(crtDec)
+	keyDec, err := base64.StdEncoding.DecodeString(cert.Key)
+	if err != nil {
+		return errors.New("base64 decoding key: " + err.Error())
+	}
+	cert.Key = string(keyDec)
+	return nil
+}
+
+func base64EncodeCertificate(cert *tc.DeliveryServiceSSLKeysCertificate) {
+	cert.CSR = base64.StdEncoding.EncodeToString([]byte(cert.CSR))
+	cert.Crt = base64.StdEncoding.EncodeToString([]byte(cert.Crt))
+	cert.Key = base64.StdEncoding.EncodeToString([]byte(cert.Key))
+}
+
 func DeleteSSLKeys(w http.ResponseWriter, r *http.Request) {
-	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"name"}, nil)
+	inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"xmlid"}, nil)
 	if userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
 	defer inf.Close()
 	if inf.Config.RiakEnabled == false {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, userErr, errors.New("deliveryservice.DeleteSSLKeys: Riak is not configured!"))
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, userErr, errors.New("deliveryservice.DeleteSSLKeys: Riak is not configured"))
+		return
+	}
+	xmlID := inf.Params["xmlid"]
+	if userErr, sysErr, errCode := tenant.Check(inf.User, xmlID, inf.Tx.Tx); userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
-	ds := tc.DeliveryServiceName(inf.Params["name"])
-	if err := riaksvc.DeleteDSSSLKeys(inf.Tx.Tx, inf.Config.RiakAuthOptions, ds, inf.Params["version"]); err != nil {
+	if err := riaksvc.DeleteDSSSLKeys(inf.Tx.Tx, inf.Config.RiakAuthOptions, xmlID, inf.Params["version"]); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, userErr, errors.New("deliveryservice.DeleteSSLKeys: deleting SSL keys: "+err.Error()))
 		return
 	}
-	api.WriteResp(w, r, "Successfully deleted ssl keys for "+string(ds))
+	api.WriteResp(w, r, "Successfully deleted ssl keys for "+xmlID)
 }
 
 // returns the cdn_id found by domainname.
@@ -216,86 +252,64 @@ WHERE r.pattern = $2
 }
 
 // verify the server certificate chain and return the
-// certificate and its chain in the proper order. Returns a  verified,
-// ordered, and base64 encoded certificate and CA chain.
-func verifyAndEncodeCertificate(certificate string, rootCA string) (string, error) {
-	var pemEncodedChain string
-	var b64crt string
+// certificate and its chain in the proper order. Returns a verified
+// and ordered certificate and CA chain.
+func verifyCertificate(certificate string, rootCA string) (string, error) {
+	// decode, verify, and order certs for storage
+	certs := strings.SplitAfter(certificate, PemCertEndMarker)
+	if len(certs) <= 1 {
+		return "", errors.New("no certificate chain to verify")
+	}
 
-	// strip newlines from encoded crt and decode it from base64.
-	crtArr := strings.Split(certificate, "\\n")
-	for i := 0; i < len(crtArr); i++ {
-		b64crt += crtArr[i]
+	// decode and verify the server certificate
+	block, _ := pem.Decode([]byte(certs[0]))
+	if block == nil {
+		return "", errors.New("could not decode pem-encoded server certificate")
 	}
-	pemCerts := make([]byte, base64.StdEncoding.EncodedLen(len(b64crt)))
-	_, err := base64.StdEncoding.Decode(pemCerts, []byte(b64crt))
+	cert, err := x509.ParseCertificate(block.Bytes)
 	if err != nil {
-		return "", fmt.Errorf("could not base64 decode the certificate %v", err)
+		return "", errors.New("could not parse the server certificate: " + err.Error())
+	}
+	if !(cert.KeyUsage&x509.KeyUsageKeyEncipherment > 0) {
+		return "", errors.New("no key encipherment usage for the server certificate")
+	}
+	bundle := ""
+	for i := 0; i < len(certs)-1; i++ {
+		bundle += certs[i]
 	}
 
-	// decode, verify, and order certs for storgae
-	var bundle string
-	certs := strings.SplitAfter(string(pemCerts), "-----END CERTIFICATE-----")
-	if len(certs) > 1 {
-		// decode and verify the server certificate
-		block, _ := pem.Decode([]byte(certs[0]))
-		cert, err := x509.ParseCertificate(block.Bytes)
-		if err != nil {
-			return "", fmt.Errorf("could not parse the server certificate %v", err)
-		}
-		if !(cert.KeyUsage&x509.KeyUsageKeyEncipherment > 0) {
-			return "", fmt.Errorf("no key encipherment usage for the server certificate")
-		}
-		for i := 0; i < len(certs)-1; i++ {
-			bundle += certs[i]
-		}
-
-		var opts x509.VerifyOptions
+	intermediatePool := x509.NewCertPool()
+	if !intermediatePool.AppendCertsFromPEM([]byte(bundle)) {
+		return "", errors.New("certificate CA bundle is empty")
+	}
 
+	opts := x509.VerifyOptions{
+		Intermediates: intermediatePool,
+	}
+	if rootCA != "" {
+		// verify the certificate chain.
 		rootPool := x509.NewCertPool()
-		if rootCA != "" {
-			if !rootPool.AppendCertsFromPEM([]byte(rootCA)) {
-				return "", fmt.Errorf("root  CA certificate is empty, %v", err)
-			}
-		}
-
-		intermediatePool := x509.NewCertPool()
-		if !intermediatePool.AppendCertsFromPEM([]byte(bundle)) {
-			return "", fmt.Errorf("certificate CA bundle is empty, %v", err)
-		}
-
-		if rootCA != "" {
-			// verify the certificate chain.
-			opts = x509.VerifyOptions{
-				Intermediates: intermediatePool,
-				Roots:         rootPool,
-			}
-		} else {
-			opts = x509.VerifyOptions{
-				Intermediates: intermediatePool,
-			}
+		if !rootPool.AppendCertsFromPEM([]byte(rootCA)) {
+			return "", errors.New("unable to parse root CA certificate")
 		}
+		opts.Roots = rootPool
+	}
 
-		chain, err := cert.Verify(opts)
-		if err != nil {
-			return "", fmt.Errorf("could verify the certificate chain %v", err)
-		}
-		if len(chain) > 0 {
-			for _, link := range chain[0] {
-				// Only print non-self signed elements of the chain
-				if link.AuthorityKeyId != nil && !bytes.Equal(link.AuthorityKeyId, link.SubjectKeyId) {
-					block := &pem.Block{Type: "CERTIFICATE", Bytes: link.Raw}
-					pemEncodedChain += string(pem.EncodeToMemory(block))
-				}
-			}
-		} else {
-			return "", fmt.Errorf("Can't find valid chain for cert in file in request")
+	chain, err := cert.Verify(opts)
+	if err != nil {
+		return "", errors.New("could not verify the certificate chain: " + err.Error())
+	}
+	if len(chain) < 1 {
+		return "", errors.New("can't find valid chain for cert in file in request")
+	}
+	pemEncodedChain := ""
+	for _, link := range chain[0] {
+		// Only print non-self signed elements of the chain
+		if link.AuthorityKeyId != nil && !bytes.Equal(link.AuthorityKeyId, link.SubjectKeyId) {
+			block := &pem.Block{Type: "CERTIFICATE", Bytes: link.Raw}
+			pemEncodedChain += string(pem.EncodeToMemory(block))
 		}
-	} else {
-		return "", fmt.Errorf("ERROR: no certificate chain to verify")
 	}
 
-	base64EncodedStr := base64.StdEncoding.EncodeToString([]byte(pemEncodedChain))
-
-	return base64EncodedStr, nil
+	return pemEncodedChain, nil
 }
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/keys_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/keys_test.go
index 8c92aa9..14b30a3 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/keys_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/keys_test.go
@@ -20,153 +20,143 @@ package deliveryservice
  */
 
 import (
-	"encoding/base64"
 	"strings"
 	"testing"
 )
 
 const (
-	BadData = "This is bad data and it is not base64 encoded"
+	BadData = "This is bad data and it is not pem encoded"
 
 	SelfSigneCertOnly = `
-LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURrakNDQW5vQ0NRQ2Z3ZDIxOUpLcFVEQU5C
-Z2txaGtpRzl3MEJBUXNGQURDQmlqRUxNQWtHQTFVRUJoTUMKVlZNeEVUQVBCZ05WQkFnTUNFTnZi
-Rzl5WVdSdk1ROHdEUVlEVlFRSERBWkVaVzUyWlhJeEVEQU9CZ05WQkFvTQpCME52YldOaGMzUXhE
-akFNQmdOVkJBc01CWFpwY0dWeU1SVXdFd1lEVlFRRERBeDNkM2N1ZEdWemRDNXZjbWN4CkhqQWNC
-Z2txaGtpRzl3MEJDUUVXRDIxcFkydGxlVUIwWlhOMExtOXlaekFlRncweE56RXhNVFl4TmpNNU1U
-TmEKRncweU56RXhNVFF4TmpNNU1UTmFNSUdLTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNB
-d0lRMjlzYjNKaApaRzh4RHpBTkJnTlZCQWNNQmtSbGJuWmxjakVRTUE0R0ExVUVDZ3dIUTI5dFky
-RnpkREVPTUF3R0ExVUVDd3dGCmRtbHdaWEl4RlRBVEJnTlZCQU1NREhkM2R5NTBaWE4wTG05eVp6
-RWVNQndHQ1NxR1NJYjNEUUVKQVJZUGJXbGoKYTJWNVFIUmxjM1F1YjNKbk1JSUJJakFOQmdrcWhr
-aUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBNm02agppQU1KcExNeFpoSVVFOURyUHZuaTA2
-TmViTFJhaDFSUzgrUjNKM1BOMVhDY2d3d1VaaWZaQnBQZ2FTYXBGWGJ2ClpkamxyZlpGcGtBZmdG
-UVhseUdhZTZCVStuNEN3TmxEZGdvMVhiRWM4cW9HSWRzN2FZSEVrclFadFp5aCtYRlkKMkJSdlM2
-Y2JHR0VjMngwdzVJa1hhMTM2V0NKY0x0QnpkVDdGQVZRSlZodTl4UFBuWGs4aWdUWmc2dEZ0MjdF
-YgplYkhDVWVEQXJHVVJGaUZXZFhtOGRHQ1BVVkNaeFNDdnh1WGxJMTVkZGdmcDlHYkZYeENVUDVW
-UjlQajZCdHFlCjdReW1GUk9zSWtscEN3NDZlYTBBODdpa1ZNYWRQQzIxVHViZmF5VUFNUTh4bDYw
-aW94NVk4OWc0WEZyRWdreWQKV3hobVBXclZwVlFyUERXS2d3SURBUUFCTUEwR0NTcUdTSWIzRFFF
-QkN3VUFBNElCQVFDWlpXNnJsbi9LYkdWbgpVWis3RFY5UFVCNHIyNUI5d3JSQUx5OHJRdzNVQWVQ
-SFJxWmlBT1ZOV3hycjM5Njd6ZFBNYzhkMWtZY2t0K2NHCkcxUFU1QmNiVVZuUzJxRTBUa2Z2cWo2
-citPaWNrcnU2dEtmWERhcU1PRXRmTmFud1Q5dURaQ1RlT2FpU0hCengKRnBQLzlURDA1Z0VZYmQx
-VzRSUnpUNi9TN3lMWTB1WWhWQUhGZGwyZTd6T2podHk0aURQRjA0ZmQrWHlaKzNXUwpJeFNnYU1y
-VHAwMG1hRnRicFBuaFRheElHek1ZdG9kaTZGYVVjT21CZk1scFJHS3lrc04wWjNlSjZSNm5oTWIz
-ClJHSjludUdMS3ZxUzV1OUJnZ2NEd28xcXYyazhNY2RTZzZwbEs2WG9kTHlNZEJWVEI2Szdod1N6
-MXVWcDNtWDEKSFZHRTQrb3UKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=`
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnoCCQCfwd219JKpUDANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMC
+VVMxETAPBgNVBAgMCENvbG9yYWRvMQ8wDQYDVQQHDAZEZW52ZXIxEDAOBgNVBAoM
+B0NvbWNhc3QxDjAMBgNVBAsMBXZpcGVyMRUwEwYDVQQDDAx3d3cudGVzdC5vcmcx
+HjAcBgkqhkiG9w0BCQEWD21pY2tleUB0ZXN0Lm9yZzAeFw0xNzExMTYxNjM5MTNa
+Fw0yNzExMTQxNjM5MTNaMIGKMQswCQYDVQQGEwJVUzERMA8GA1UECAwIQ29sb3Jh
+ZG8xDzANBgNVBAcMBkRlbnZlcjEQMA4GA1UECgwHQ29tY2FzdDEOMAwGA1UECwwF
+dmlwZXIxFTATBgNVBAMMDHd3dy50ZXN0Lm9yZzEeMBwGCSqGSIb3DQEJARYPbWlj
+a2V5QHRlc3Qub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6m6j
+iAMJpLMxZhIUE9DrPvni06NebLRah1RS8+R3J3PN1XCcgwwUZifZBpPgaSapFXbv
+ZdjlrfZFpkAfgFQXlyGae6BU+n4CwNlDdgo1XbEc8qoGIds7aYHEkrQZtZyh+XFY
+2BRvS6cbGGEc2x0w5IkXa136WCJcLtBzdT7FAVQJVhu9xPPnXk8igTZg6tFt27Eb
+ebHCUeDArGURFiFWdXm8dGCPUVCZxSCvxuXlI15ddgfp9GbFXxCUP5VR9Pj6Btqe
+7QymFROsIklpCw46ea0A87ikVMadPC21TubfayUAMQ8xl60iox5Y89g4XFrEgkyd
+WxhmPWrVpVQrPDWKgwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCZZW6rln/KbGVn
+UZ+7DV9PUB4r25B9wrRALy8rQw3UAePHRqZiAOVNWxrr3967zdPMc8d1kYckt+cG
+G1PU5BcbUVnS2qE0Tkfvqj6r+Oickru6tKfXDaqMOEtfNanwT9uDZCTeOaiSHBzx
+FpP/9TD05gEYbd1W4RRzT6/S7yLY0uYhVAHFdl2e7zOjhty4iDPF04fd+XyZ+3WS
+IxSgaMrTp00maFtbpPnhTaxIGzMYtodi6FaUcOmBfMlpRGKyksN0Z3eJ6R6nhMb3
+RGJ9nuGLKvqS5u9BggcDwo1qv2k8McdSg6plK6XodLyMdBVTB6K7hwSz1uVp3mX1
+HVGE4+ou
+-----END CERTIFICATE-----
+`
 	GoodTLSKeys = `
-LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUYzRENDQThTZ0F3SUJBZ0lDRUFBd0RRWUpL
-b1pJaHZjTkFRRUxCUUF3Z1l3eEN6QUpCZ05WQkFZVEFsVlQKTVJFd0R3WURWUVFJREFoRGIyeHZj
-bUZrYnpFa01DSUdBMVVFQ2d3YlNYQmpaRzRnUTJWeWRHbG1hV05oZEdVZwpRWFYwYUc5eWFYUjVN
-U1F3SWdZRFZRUUxEQnRKY0dOa2JpQkRaWEowYVdacFkyRjBaU0JCZFhSb2IzSnBkSGt4CkhqQWNC
-Z05WQkFNTUZVbHdZMlJ1SUVsdWRHVnliV1ZrYVdGMFpTQkRRVEFlRncweE56RXhNVFl5TURVd01U
-bGEKRncweU5qQXlNREl5TURVd01UbGFNSEF4Q3pBSkJnTlZCQVlUQWxWVE1SRXdEd1lEVlFRSURB
-aERiMnh2Y21GawpiekVQTUEwR0ExVUVCd3dHUkdWdWRtVnlNUTh3RFFZRFZRUUtEQVpKY0dOa2Jp
-QXhFakFRQmdOVkJBc01DVWx3ClkyUnVJR1JsZGpFWU1CWUdBMVVFQXd3UGQzZDNMbVY0WVcxd2JH
-VXVZMjl0TUlJQklqQU5CZ2txaGtpRzl3MEIKQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMWg5Zlh3
-SjE3UVlOTVhNNWpFZ0hlY1Z4V20rQ05QUWJpMk8wYUxtNQpsYUV1b2N0bC94bmNsRGdrT2NWNTBz
-SWdVUlNuYVJYSENNYTkzemI5NXhRZWZUSXZSRi8xa1U5ZTY4bW1Gano4CmZyQWRPYU05MFRkVWYx
-eXYyNlczN25UOUR4MjZDQWlTd0FMWFZkeCs0b1Bvck5IdjIweDNxUGJzKzNjRHVuQWYKL1hWWis2
-dHhuOVZzZzAwb2RIWm9mcVpibUdUdkcveWJRY0dQalJQYVdZSGFMYWZqcERCM3dQc0REdTBMMGJY
-VApITlE4Vld6S3drOFVRdHpndEt4WEFFMlp3TFNkUVdzV0VLSEZGTmthT3F3ZnEyMVVZdmN0NHEy
-cnE5MXREbmJhCmZxTWpOTnNxVmtYZFIyNFdjUDhtZ1YxY3NWTkx1WmhtUWFCTlo2dytoR2l0aVFJ
-REFRQUJvNElCWVRDQ0FWMHcKQ1FZRFZSMFRCQUl3QURBUkJnbGdoa2dCaHZoQ0FRRUVCQU1DQmtB
-d013WUpZSVpJQVliNFFnRU5CQ1lXSkU5dwpaVzVUVTB3Z1IyVnVaWEpoZEdWa0lGTmxjblpsY2lC
-RFpYSjBhV1pwWTJGMFpUQWRCZ05WSFE0RUZnUVVpWmVaCjVVbXdGRjFWQ2dwd1hmM2crcTFGUzZN
-d2djTUdBMVVkSXdTQnV6Q0J1SUFVSVY3aXF0Myt4WWExRngvbll6eGQKMWh4dk53MmhnWnVrZ1pn
-d2daVXhDekFKQmdOVkJBWVRBbFZUTVJFd0R3WURWUVFJREFoRGIyeHZjbUZrYnpFUApNQTBHQTFV
-RUJ3d0dSR1Z1ZG1WeU1TUXdJZ1lEVlFRS0RCdEpjR05rYmlCRFpYSjBhV1pwWTJGMFpTQkJkWFJv
-CmIzSnBkSGt4SkRBaUJnTlZCQXNNRzBsd1kyUnVJRU5sY25ScFptbGpZWFJsSUdGMWRHaHZjbWww
-ZVRFV01CUUcKQTFVRUF3d05TWEJqWkc0Z1VtOXZkQ0JEUVlJQ0VBQXdEZ1lEVlIwUEFRSC9CQVFE
-QWdXZ01CTUdBMVVkSlFRTQpNQW9HQ0NzR0FRVUZCd01CTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElD
-QVFCSFgrWW1PNlRMYk1UWkJ5dXdyZ2lICnV5N3JQenBYVFhTSnZVTSt1cHhTZW5ybXo4SDVmQnp5
-cExmS0UxTk9LNThScjI1T3lBZDFJcWdHMUlkKzh5Y2cKYXRldGpJMXBvYU5DWnZLUFJyNDh3amNU
-VnNmZ3g3SFhXMGZ5a0kwS1QyMzlPazR1c0VNM3VxbWJrbjdBbm5mNwpkYnk4Qkt6NHA5bjF5ZTdl
-SHBpcHUxQTVXeGpCUWVEeW1sUHcvQ005RTB2NUYvaGdpV3hXK2U4bDV1N2g0M2JQCjFCTk82UEpY
-Si9HT05pUWg0YmNMMFI3NjN0TklLR1ZOWUhTZmZXNzF2eW9ZNnJlWGRLQk5uazkxT2dqRndIVE4K
-QUxKdHI5ank1eHk5dktBVFk3Z1RoVldEaFcvdWl2OExVSUN4eFpXbkN1Y0t2aGdCMmJMV2FIeElP
-cjlZeWphWApoRVZkSGdUVlhQbDVjTVhCdytVWkxuM3Z3NUVRSHVNRkFJV1IyWk5JaHJFdmphUG9u
-NXFhRXZlUUNEc2RhMzl3CnJUcjNsaGV5QkdNaFRubUE0d05WK2xOU0xJbUFjMm83OGFldGUwY1Rz
-SjYxOXNuR09EVW85UU8yWVovazd2UFMKbUhlSzBWclp2bkU4dWtsRkpVVERheUxvRCtTbnN4enRS
-SVR2ay9FdVJheTJQMWZHMS9xWm43UVVJZytaTTRhVgpTOHZQcmZJOXJ3dThDUmd4bVRUdFg1Wk1l
-ejg5Znk2RmxWRElUalZOaFJyVkljMm0zeEwwM0FTVGI2TmFua0pvCkVuK01MUXBDRlZLeVF5OU43
-MlJaTno2OEtpUEgvZnJhQWRxNG1MNU5BY1I0UC82R2c3U24rcWpuZi80bDQzeloKKzd1enFqd2hk
-dFUvWUhKOWo4L2Nvdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJU
-SUZJQ0FURS0tLS0tCk1JSUdCVENDQSsyZ0F3SUJBZ0lDRUFBd0RRWUpLb1pJaHZjTkFRRUxCUUF3
-Z1pVeEN6QUpCZ05WQkFZVEFsVlQKTVJFd0R3WURWUVFJREFoRGIyeHZjbUZrYnpFUE1BMEdBMVVF
-Qnd3R1JHVnVkbVZ5TVNRd0lnWURWUVFLREJ0SgpjR05rYmlCRFpYSjBhV1pwWTJGMFpTQkJkWFJv
-YjNKcGRIa3hKREFpQmdOVkJBc01HMGx3WTJSdUlFTmxjblJwClptbGpZWFJsSUdGMWRHaHZjbWww
-ZVRFV01CUUdBMVVFQXd3TlNYQmpaRzRnVW05dmRDQkRRVEFlRncweE56RXgKTVRZeU1ETTRNVFZh
-RncweU56RXhNVFF5TURNNE1UVmFNSUdNTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRQpDQXdJ
-UTI5c2IzSmhaRzh4SkRBaUJnTlZCQW9NRzBsd1kyUnVJRU5sY25ScFptbGpZWFJsSUVGMWRHaHZj
-bWwwCmVURWtNQ0lHQTFVRUN3d2JTWEJqWkc0Z1EyVnlkR2xtYVdOaGRHVWdRWFYwYUc5eWFYUjVN
-UjR3SEFZRFZRUUQKREJWSmNHTmtiaUJKYm5SbGNtMWxaR2xoZEdVZ1EwRXdnZ0lpTUEwR0NTcUdT
-SWIzRFFFQkFRVUFBNElDRHdBdwpnZ0lLQW9JQ0FRREtreFE5aGRNdFN3Zk5FQnFHUEFLenU3Q1Bo
-SCt4dTNrTDRlK3JKMDlFQisrZnhHOTA1bXdNClltOHMxR0MvaTFWMWhQYk9rK3pMV1hjY1podEM0
-OUJ0dHNEQlZSbWFndDRxNmVlRDEyQXh6VkJveldqNFluRnkKWkUwNENpWmxBSU4zcU40VG5OVC9P
-M2l5dm1qNThRRElGVk81MVlOU3JyN2oyZFFSTHBveVM2czg3a3B3OUE2VAoyNEwxcExrbUZ1QWdD
-TE1GUEc1SFpXeVpTU3RwWE96T2M3TElUWlFYUXp1bndMYXpOOVo0QXo4ellDOWlsTzZWCnROTk5j
-K1k3TXBGclRhRUZGU3NNMitSRWV4dVB0Q090VC9aRWNPd1A4ODRUNkFDY1VTVHY4NmlFL0VGT1Bn
-WmgKdlRLMkQwTnphdDNaVHNjNU4ydnZOMGVabTZDT25WQXZZTndyVFdHNHYzWVV0THIvUEVvRm05
-bXRQZFNBK1RzaQpMa0dGUmp3QW9BbmhWaWVGQUZ1bFFuc3diWkFhSlJjL3hTN0JKdnRzLzNKOWk3
-bDFvcHF1MEVibTZMOGpMZUh2CnAvb3AxVEQ4TElRa2NwN0dkc1hrNExZSDZWb3BhTk9pOHlvYUVm
-S1d3clhoeEJkQ2hISGxZQ2NmZWN5RDhPMzkKOGV1b0dRMHppL2VhQ3IzTUhZVTErYTIrNVRSUGR6
-SHgvbC8zVjF3eFg5aG1PR2Nyd1RDdGNZUnpSanphMlVsRQpVYWNtd0JxR3lmYTFHbi9pT3RvOXlx
-dFJ2V2xQR3E5ekpOUXpOQko2aVdSN2d5YjB0ODIwanBkdG1GeU5CNVB2CkpjZUh5VnlpeC83c0JV
-QjVHd01PMGlHbkxQcFk5SXg4N25ZUDNpdE4zamtxYWhUclQ3dEh1d0lEQVFBQm8yWXcKWkRBZEJn
-TlZIUTRFRmdRVUlWN2lxdDMreFlhMUZ4L25ZenhkMWh4dk53MHdId1lEVlIwakJCZ3dGb0FVV2Nk
-SQpCUjZ0M044dzd5VXQvVE9mWHdtdWJBZ3dFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFPQmdO
-VkhROEJBZjhFCkJBTUNBWVl3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCQUtiZDhuS0ExaGx4Q1Rj
-elRnOVBIU2RxSFZFWVdxUkQKZHpyb09uejBLY0lnSkY0bmNSYU4zbm5hNzR3SW0zbkpEL1d1d2RG
-OGRWYnBENFlhZVpLaFFnbVhtWWdnVkgwbAo4SUlrZEMzcVlOVERqNHhoZzdxWkMzcHpLdU5JZzd5
-SHYzQXhyL3JiOUJ2cFVKeGJLby9hbG4vRzZQcmJOb1F4CjR4YjZ4bEs2V2NZT1JCL1VnekY2aktN
-NUNCWGFVdDJRUW1XdHM3TWdub056WjRreHNZYnV3ajNyT1B6SElnUlkKWXdKdCtraDV0bHloSFBr
-Z2p3VVRiTWcvTXYvb21melozK1hNQkQyWjY5ZU5kOVJIRTlYUzZBZE1LdERpcHJoZwpUTHUrOXRP
-Vmx5ZFFqWmszS3NxRnY1Ny8xVHdLcnQ5SkZqWGsvUFBRYWxYb1Azb3lrb2l2aWFjZDRNeWlCbWFX
-Cmk1V2J0Y3hHUDV2d2FObjI2N09HbGliZlFtUWdBMVZJZ0RxZnRyUXdlNlQvWndPS1hxRnFiNTZn
-cWRXNFZkT1UKNWJHTjB1TFhlbXJpdU5Rb2xUcFZtaE51MGJOSTVXQ2lZN2MzMFhOMk01ZWJjQldE
-SE9yNERPQ2crbW1YK3RKVApHaTFqdmZtQTZMT1lueVE4eEFWMDgwdVZlMzVOUTEwZTlteTZMdVhN
-aVE3dWJ2SFVTNU53YVFDRnBMREl2YmhmCjJYVmM0Nk5kMFYvYm9sTTIzQVFSbHp1bzBJU3NEU2VX
-U1FWTSt1UVorcExlOUc1bjJNeHFGZDg0VnU0ODlhdWMKMDJJOWNIaU5haFd5WFB0dm52TVd3SW1J
-QjMwQ1loaDVCeUdXbmVEbzNkNVpqMkhBQTZGd082N0xsSWhTZTdxegpheER0Y000bk8zUUgKLS0t
-LS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJR0Vq
-Q0NBL3FnQXdJQkFnSUpBTDJWMWhuTFd1emdNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1JR1ZNUXN3Q1FZ
-RApWUVFHRXdKVlV6RVJNQThHQTFVRUNBd0lRMjlzYjNKaFpHOHhEekFOQmdOVkJBY01Ca1JsYm5a
-bGNqRWtNQ0lHCkExVUVDZ3diU1hCalpHNGdRMlZ5ZEdsbWFXTmhkR1VnUVhWMGFHOXlhWFI1TVNR
-d0lnWURWUVFMREJ0SmNHTmsKYmlCRFpYSjBhV1pwWTJGMFpTQmhkWFJvYjNKcGRIa3hGakFVQmdO
-VkJBTU1EVWx3WTJSdUlGSnZiM1FnUTBFdwpIaGNOTVRjeE1URTJNakF4T1RJMFdoY05NamN4TVRF
-ME1qQXhPVEkwV2pDQmxURUxNQWtHQTFVRUJoTUNWVk14CkVUQVBCZ05WQkFnTUNFTnZiRzl5WVdS
-dk1ROHdEUVlEVlFRSERBWkVaVzUyWlhJeEpEQWlCZ05WQkFvTUcwbHcKWTJSdUlFTmxjblJwWm1s
-allYUmxJRUYxZEdodmNtbDBlVEVrTUNJR0ExVUVDd3diU1hCalpHNGdRMlZ5ZEdsbQphV05oZEdV
-Z1lYVjBhRzl5YVhSNU1SWXdGQVlEVlFRRERBMUpjR05rYmlCU2IyOTBJRU5CTUlJQ0lqQU5CZ2tx
-CmhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBMkhzTGtubTl3eDFWMzN6dHRNV24xc0ZF
-SnlMTk1lZVcKaEEwZHdlS0NocURWdzVsQXlJaWQwbHFqbmpJRWl2bEFNOHpkMk5BUFpUSEZHOUJU
-M3VuZHVOY0RjV3VSZ2gzMwptbHlzc0VoYXJXdEU2VTdsenc4Uk1HV2t5V1FrSjJFMFN4akUvVm1L
-UWpIMy80QWs4U2hoVFpGS0VadXdlUmRnCmoyMThxVWVtc09WK0VOVHNuR1V4b0FQcHI1Y0dHbzRp
-Z2ZPOXRwSTFnN1BXbmtZclZGdHdzUG95MkNLeHFoL0kKM0Y2N0VacTJ5Uk9CeTlnQmhDNUZ1Wmh1
-dmdwdHZTYWtOUXkvdys5YnVSZmZzaGI2OXdMc2JxUWF5aGpsTHFjUQpYa2NoNHk2Y0c4WWxnK2hy
-cXptRzN2RzRxMDNZRno4UHVNZC91TnhlSlFBYlpKL3NYaDdqVlFOSml0Z2k2b095CmoxeFZ6TktV
-cWIxTXgyNURoQnkxMjUvbDh3R0Jkc0NSVVlSNnI3ME5BWlJuZGprUUtWNGwzdFRRNCtvMnJkamQK
-Qy8yU2syL2JIVmJ5dE1xNEl6KzFzT3NJVVYzTGErUitEV2NMQTZVb25wRW9jTmI4YnQ1YnU0SmhS
-RHJuSndQaQo0alJhSCtLZ3hwQlRJbFozYUZuU3MwSTN3a1BPZ0EzRHJJNkxXeGd0M0JmUHVLOURQ
-bWExcmM0QjFjd1NRVE5rCnl5VVkyYm9Yc25MTU1QZWJGM2Rqd2J0WUVkcUFhaXh4enpRQ1ZZbGhm
-Q0VRanFjZVNkNlRKMlNkekg0STNKN3gKT3NMejFMai85MmFEZWVxNzdxL2M5RmlkbmZOZTc1ekI3
-Z1hOL2JUdGI1NVpjVFRPOFhBRXZlZ3hjcnc1Q25PMQpNbkZ3NnhEZisrVUNBd0VBQWFOak1HRXdI
-UVlEVlIwT0JCWUVGRm5IU0FVZXJkemZNTzhsTGYwem4xOEpybXdJCk1COEdBMVVkSXdRWU1CYUFG
-Rm5IU0FVZXJkemZNTzhsTGYwem4xOEpybXdJTUE4R0ExVWRFd0VCL3dRRk1BTUIKQWY4d0RnWURW
-UjBQQVFIL0JBUURBZ0dHTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElDQVFCK2FZV1poKy9nQ21GeQpv
-STU4MkFDNSs1NGM3eGVMUm10UVBtRnBBQTJZNGlUU2VHeWp2Mmc1TTlhZ1E0SXh3dWU1RGx1UWxz
-RGNEc0p3CmxzWFZIeUFsQ2Y2bkRiSk9wZjVtNml4ZnZCRitRekVlWGpVRzc0aVNDV3JBcFlGbXNO
-c0NybHNNL0VQSm9ncXUKN2NnR3ZId1dZTmpQenV3b1UwQVdITzlGZ0liRHozTWMxNVpsSElRMlQv
-aXh4eUJmcElQYzVEOTFrNHUvWldTYgpoTzZsc00xaUVBdnY5VG9VWGtLdHRDUTBmTjM0QndZWDQ3
-QXhDNzF0U3Q5L1ZLSURJRVhqenFKSnBiSmRCR1NtCld1aVpIV3dCelZ3N2s1eStTK3dNNXZaL09N
-dzB0aEwrSTUzem1reEdFZjN0TUg0UE9lS3ZYL3UxOW4wVEZrLzEKOTZjenRWbVRCNlc2N21iOXpa
-OHlTZUp0RDl1WEU0NHljeGs5M2dUbmFya0lHZFRmOFhvbkNWbGtIR3BiNlRXTQo1RTdhV0NvVmJx
-UVJrRjFycVB6YThoUkpHSDFFR2ttZGJrdXlVTUxtUTFGSks5Zi94RkljcmRCR3BNbHh5T29MCk1T
-ZUZlYWdvU0xOcTV4emp1Y0YvNm1rQVVKOFVaQXhyU0dtYURFYVN0MS9xSlJIb3ZIY3QyYjVGV1pM
-dVJ0MGcKZkRzR29LVDhJRnVhcFNzbUZFSW52RDBXS1hEU1BsVThyVHkxWkJEbENxcDNlTkg0dENm
-TGFoazd2TEEwSmJmNwpVTHJ0RG5yTmZqeFVZRmNGMFJjYXVSMWRsclFRUkFzMzFRT3pYK0pPcjRn
-VUc3eVhhM2o2NG54Mkc1TXBMZ1NvCk9FVWpmYWtLNzErVi9IYlF0NDc3elI0azdjUmJpQT09Ci0t
-LS0tRU5EIENFUlRJRklDQVRFLS0tLS0K`
-
+-----BEGIN CERTIFICATE-----
+MIIF3DCCA8SgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT
+MREwDwYDVQQIDAhDb2xvcmFkbzEkMCIGA1UECgwbSXBjZG4gQ2VydGlmaWNhdGUg
+QXV0aG9yaXR5MSQwIgYDVQQLDBtJcGNkbiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkx
+HjAcBgNVBAMMFUlwY2RuIEludGVybWVkaWF0ZSBDQTAeFw0xNzExMTYyMDUwMTla
+Fw0yNjAyMDIyMDUwMTlaMHAxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhDb2xvcmFk
+bzEPMA0GA1UEBwwGRGVudmVyMQ8wDQYDVQQKDAZJcGNkbiAxEjAQBgNVBAsMCUlw
+Y2RuIGRldjEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA1h9fXwJ17QYNMXM5jEgHecVxWm+CNPQbi2O0aLm5
+laEuoctl/xnclDgkOcV50sIgURSnaRXHCMa93zb95xQefTIvRF/1kU9e68mmFjz8
+frAdOaM90TdUf1yv26W37nT9Dx26CAiSwALXVdx+4oPorNHv20x3qPbs+3cDunAf
+/XVZ+6txn9Vsg00odHZofqZbmGTvG/ybQcGPjRPaWYHaLafjpDB3wPsDDu0L0bXT
+HNQ8VWzKwk8UQtzgtKxXAE2ZwLSdQWsWEKHFFNkaOqwfq21UYvct4q2rq91tDnba
+fqMjNNsqVkXdR24WcP8mgV1csVNLuZhmQaBNZ6w+hGitiQIDAQABo4IBYTCCAV0w
+CQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9w
+ZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUiZeZ
+5UmwFF1VCgpwXf3g+q1FS6MwgcMGA1UdIwSBuzCBuIAUIV7iqt3+xYa1Fx/nYzxd
+1hxvNw2hgZukgZgwgZUxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhDb2xvcmFkbzEP
+MA0GA1UEBwwGRGVudmVyMSQwIgYDVQQKDBtJcGNkbiBDZXJ0aWZpY2F0ZSBBdXRo
+b3JpdHkxJDAiBgNVBAsMG0lwY2RuIENlcnRpZmljYXRlIGF1dGhvcml0eTEWMBQG
+A1UEAwwNSXBjZG4gUm9vdCBDQYICEAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQM
+MAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQBHX+YmO6TLbMTZByuwrgiH
+uy7rPzpXTXSJvUM+upxSenrmz8H5fBzypLfKE1NOK58Rr25OyAd1IqgG1Id+8ycg
+atetjI1poaNCZvKPRr48wjcTVsfgx7HXW0fykI0KT239Ok4usEM3uqmbkn7Annf7
+dby8BKz4p9n1ye7eHpipu1A5WxjBQeDymlPw/CM9E0v5F/hgiWxW+e8l5u7h43bP
+1BNO6PJXJ/GONiQh4bcL0R763tNIKGVNYHSffW71vyoY6reXdKBNnk91OgjFwHTN
+ALJtr9jy5xy9vKATY7gThVWDhW/uiv8LUICxxZWnCucKvhgB2bLWaHxIOr9YyjaX
+hEVdHgTVXPl5cMXBw+UZLn3vw5EQHuMFAIWR2ZNIhrEvjaPon5qaEveQCDsda39w
+rTr3lheyBGMhTnmA4wNV+lNSLImAc2o78aete0cTsJ619snGODUo9QO2YZ/k7vPS
+mHeK0VrZvnE8uklFJUTDayLoD+SnsxztRITvk/EuRay2P1fG1/qZn7QUIg+ZM4aV
+S8vPrfI9rwu8CRgxmTTtX5ZMez89fy6FlVDITjVNhRrVIc2m3xL03ASTb6NankJo
+En+MLQpCFVKyQy9N72RZNz68KiPH/fraAdq4mL5NAcR4P/6Gg7Sn+qjnf/4l43zZ
++7uzqjwhdtU/YHJ9j8/cow==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGBTCCA+2gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT
+MREwDwYDVQQIDAhDb2xvcmFkbzEPMA0GA1UEBwwGRGVudmVyMSQwIgYDVQQKDBtJ
+cGNkbiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxJDAiBgNVBAsMG0lwY2RuIENlcnRp
+ZmljYXRlIGF1dGhvcml0eTEWMBQGA1UEAwwNSXBjZG4gUm9vdCBDQTAeFw0xNzEx
+MTYyMDM4MTVaFw0yNzExMTQyMDM4MTVaMIGMMQswCQYDVQQGEwJVUzERMA8GA1UE
+CAwIQ29sb3JhZG8xJDAiBgNVBAoMG0lwY2RuIENlcnRpZmljYXRlIEF1dGhvcml0
+eTEkMCIGA1UECwwbSXBjZG4gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR4wHAYDVQQD
+DBVJcGNkbiBJbnRlcm1lZGlhdGUgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDKkxQ9hdMtSwfNEBqGPAKzu7CPhH+xu3kL4e+rJ09EB++fxG905mwM
+Ym8s1GC/i1V1hPbOk+zLWXccZhtC49BttsDBVRmagt4q6eeD12AxzVBozWj4YnFy
+ZE04CiZlAIN3qN4TnNT/O3iyvmj58QDIFVO51YNSrr7j2dQRLpoyS6s87kpw9A6T
+24L1pLkmFuAgCLMFPG5HZWyZSStpXOzOc7LITZQXQzunwLazN9Z4Az8zYC9ilO6V
+tNNNc+Y7MpFrTaEFFSsM2+REexuPtCOtT/ZEcOwP884T6ACcUSTv86iE/EFOPgZh
+vTK2D0Nzat3ZTsc5N2vvN0eZm6COnVAvYNwrTWG4v3YUtLr/PEoFm9mtPdSA+Tsi
+LkGFRjwAoAnhVieFAFulQnswbZAaJRc/xS7BJvts/3J9i7l1opqu0Ebm6L8jLeHv
+p/op1TD8LIQkcp7GdsXk4LYH6VopaNOi8yoaEfKWwrXhxBdChHHlYCcfecyD8O39
+8euoGQ0zi/eaCr3MHYU1+a2+5TRPdzHx/l/3V1wxX9hmOGcrwTCtcYRzRjza2UlE
+UacmwBqGyfa1Gn/iOto9yqtRvWlPGq9zJNQzNBJ6iWR7gyb0t820jpdtmFyNB5Pv
+JceHyVyix/7sBUB5GwMO0iGnLPpY9Ix87nYP3itN3jkqahTrT7tHuwIDAQABo2Yw
+ZDAdBgNVHQ4EFgQUIV7iqt3+xYa1Fx/nYzxd1hxvNw0wHwYDVR0jBBgwFoAUWcdI
+BR6t3N8w7yUt/TOfXwmubAgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
+BAMCAYYwDQYJKoZIhvcNAQELBQADggIBAKbd8nKA1hlxCTczTg9PHSdqHVEYWqRD
+dzroOnz0KcIgJF4ncRaN3nna74wIm3nJD/WuwdF8dVbpD4YaeZKhQgmXmYggVH0l
+8IIkdC3qYNTDj4xhg7qZC3pzKuNIg7yHv3Axr/rb9BvpUJxbKo/aln/G6PrbNoQx
+4xb6xlK6WcYORB/UgzF6jKM5CBXaUt2QQmWts7MgnoNzZ4kxsYbuwj3rOPzHIgRY
+YwJt+kh5tlyhHPkgjwUTbMg/Mv/omfzZ3+XMBD2Z69eNd9RHE9XS6AdMKtDiprhg
+TLu+9tOVlydQjZk3KsqFv57/1TwKrt9JFjXk/PPQalXoP3oykoiviacd4MyiBmaW
+i5WbtcxGP5vwaNn267OGlibfQmQgA1VIgDqftrQwe6T/ZwOKXqFqb56gqdW4VdOU
+5bGN0uLXemriuNQolTpVmhNu0bNI5WCiY7c30XN2M5ebcBWDHOr4DOCg+mmX+tJT
+Gi1jvfmA6LOYnyQ8xAV080uVe35NQ10e9my6LuXMiQ7ubvHUS5NwaQCFpLDIvbhf
+2XVc46Nd0V/bolM23AQRlzuo0ISsDSeWSQVM+uQZ+pLe9G5n2MxqFd84Vu489auc
+02I9cHiNahWyXPtvnvMWwImIB30CYhh5ByGWneDo3d5Zj2HAA6FwO67LlIhSe7qz
+axDtcM4nO3QH
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGEjCCA/qgAwIBAgIJAL2V1hnLWuzgMA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD
+VQQGEwJVUzERMA8GA1UECAwIQ29sb3JhZG8xDzANBgNVBAcMBkRlbnZlcjEkMCIG
+A1UECgwbSXBjZG4gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSQwIgYDVQQLDBtJcGNk
+biBDZXJ0aWZpY2F0ZSBhdXRob3JpdHkxFjAUBgNVBAMMDUlwY2RuIFJvb3QgQ0Ew
+HhcNMTcxMTE2MjAxOTI0WhcNMjcxMTE0MjAxOTI0WjCBlTELMAkGA1UEBhMCVVMx
+ETAPBgNVBAgMCENvbG9yYWRvMQ8wDQYDVQQHDAZEZW52ZXIxJDAiBgNVBAoMG0lw
+Y2RuIENlcnRpZmljYXRlIEF1dGhvcml0eTEkMCIGA1UECwwbSXBjZG4gQ2VydGlm
+aWNhdGUgYXV0aG9yaXR5MRYwFAYDVQQDDA1JcGNkbiBSb290IENBMIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2HsLknm9wx1V33zttMWn1sFEJyLNMeeW
+hA0dweKChqDVw5lAyIid0lqjnjIEivlAM8zd2NAPZTHFG9BT3unduNcDcWuRgh33
+mlyssEharWtE6U7lzw8RMGWkyWQkJ2E0SxjE/VmKQjH3/4Ak8ShhTZFKEZuweRdg
+j218qUemsOV+ENTsnGUxoAPpr5cGGo4igfO9tpI1g7PWnkYrVFtwsPoy2CKxqh/I
+3F67EZq2yROBy9gBhC5FuZhuvgptvSakNQy/w+9buRffshb69wLsbqQayhjlLqcQ
+Xkch4y6cG8Ylg+hrqzmG3vG4q03YFz8PuMd/uNxeJQAbZJ/sXh7jVQNJitgi6oOy
+j1xVzNKUqb1Mx25DhBy125/l8wGBdsCRUYR6r70NAZRndjkQKV4l3tTQ4+o2rdjd
+C/2Sk2/bHVbytMq4Iz+1sOsIUV3La+R+DWcLA6UonpEocNb8bt5bu4JhRDrnJwPi
+4jRaH+KgxpBTIlZ3aFnSs0I3wkPOgA3DrI6LWxgt3BfPuK9DPma1rc4B1cwSQTNk
+yyUY2boXsnLMMPebF3djwbtYEdqAaixxzzQCVYlhfCEQjqceSd6TJ2SdzH4I3J7x
+OsLz1Lj/92aDeeq77q/c9FidnfNe75zB7gXN/bTtb55ZcTTO8XAEvegxcrw5CnO1
+MnFw6xDf++UCAwEAAaNjMGEwHQYDVR0OBBYEFFnHSAUerdzfMO8lLf0zn18JrmwI
+MB8GA1UdIwQYMBaAFFnHSAUerdzfMO8lLf0zn18JrmwIMA8GA1UdEwEB/wQFMAMB
+Af8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQB+aYWZh+/gCmFy
+oI582AC5+54c7xeLRmtQPmFpAA2Y4iTSeGyjv2g5M9agQ4Ixwue5DluQlsDcDsJw
+lsXVHyAlCf6nDbJOpf5m6ixfvBF+QzEeXjUG74iSCWrApYFmsNsCrlsM/EPJogqu
+7cgGvHwWYNjPzuwoU0AWHO9FgIbDz3Mc15ZlHIQ2T/ixxyBfpIPc5D91k4u/ZWSb
+hO6lsM1iEAvv9ToUXkKttCQ0fN34BwYX47AxC71tSt9/VKIDIEXjzqJJpbJdBGSm
+WuiZHWwBzVw7k5y+S+wM5vZ/OMw0thL+I53zmkxGEf3tMH4POeKvX/u19n0TFk/1
+96cztVmTB6W67mb9zZ8ySeJtD9uXE44ycxk93gTnarkIGdTf8XonCVlkHGpb6TWM
+5E7aWCoVbqQRkF1rqPza8hRJGH1EGkmdbkuyUMLmQ1FJK9f/xFIcrdBGpMlxyOoL
+MSeFeagoSLNq5xzjucF/6mkAUJ8UZAxrSGmaDEaSt1/qJRHovHct2b5FWZLuRt0g
+fDsGoKT8IFuapSsmFEInvD0WKXDSPlU8rTy1ZBDlCqp3eNH4tCfLahk7vLA0Jbf7
+ULrtDnrNfjxUYFcF0RcauR1dlrQQRAs31QOzX+JOr4gUG7yXa3j64nx2G5MpLgSo
+OEUjfakK71+V/HbQt477zR4k7cRbiA==
+-----END CERTIFICATE-----
+`
 	rootCA = `
 -----BEGIN CERTIFICATE-----
 MIIGEjCCA/qgAwIBAgIJAL2V1hnLWuzgMA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD
@@ -209,32 +199,26 @@ OEUjfakK71+V/HbQt477zR4k7cRbiA==
 func TestVerifyAndEncodeCertificate(t *testing.T) {
 
 	// should fail bad base64 data
-	dat, err := verifyAndEncodeCertificate(BadData, "")
+	dat, err := verifyCertificate(BadData, "")
 	if err == nil {
 		t.Errorf("Unexpected result, there should have been a base64 decoding failure")
 	}
 
 	// should fail, can't verify self signed cert
-	dat, err = verifyAndEncodeCertificate(SelfSigneCertOnly, rootCA)
+	dat, err = verifyCertificate(SelfSigneCertOnly, rootCA)
 	if err == nil {
 		t.Errorf("Unexpected result, a certificate verification error should have occured")
 	}
 
 	// should pass
-	dat, err = verifyAndEncodeCertificate(GoodTLSKeys, rootCA)
+	dat, err = verifyCertificate(GoodTLSKeys, rootCA)
 	if err != nil {
 		t.Errorf("Test failure: %s", err)
 	}
 
-	pemCerts := make([]byte, base64.StdEncoding.EncodedLen(len(dat)))
-	_, err = base64.StdEncoding.Decode(pemCerts, []byte(dat))
-	if err != nil {
-		t.Errorf("Test failure: bad retrun value from verifyAndEncodeCertificate(): %v", err)
-	}
-
-	certs := strings.SplitAfter(string(pemCerts), "-----END CERTIFICATE-----")
+	certs := strings.SplitAfter(dat, PemCertEndMarker)
 	length := len(certs) - 1
 	if length != 2 {
-		t.Errorf("Test failure: expected 2 certs from verifyAndEncodeCertificate(), got: %d ", length)
+		t.Errorf("Test failure: expected 2 certs from verifyCertificate(), got: %d ", length)
 	}
 }
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
index d313608..16b87cf 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
@@ -75,12 +75,12 @@ func generatePutRiakKeys(req tc.DeliveryServiceSSLKeysReq, tx *sql.Tx, cfg *conf
 		}
 		dsSSLKeys.Certificate = tc.DeliveryServiceSSLKeysCertificate{Crt: string(crt), Key: string(key), CSR: string(csr)}
 	}
-	if err := riaksvc.PutDeliveryServiceSSLKeysObjTx(dsSSLKeys, tx, cfg.RiakAuthOptions); err != nil {
+	if err := riaksvc.PutDeliveryServiceSSLKeysObj(dsSSLKeys, tx, cfg.RiakAuthOptions); err != nil {
 		return errors.New("putting riak keys: " + err.Error())
 	}
 
 	dsSSLKeys.Version = riaksvc.DSSSLKeyVersionLatest
-	if err := riaksvc.PutDeliveryServiceSSLKeysObjTx(dsSSLKeys, tx, cfg.RiakAuthOptions); err != nil {
+	if err := riaksvc.PutDeliveryServiceSSLKeysObj(dsSSLKeys, tx, cfg.RiakAuthOptions); err != nil {
 		return errors.New("putting latest riak keys: " + err.Error())
 	}
 	return nil
diff --git a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
index 24f3d57..1ad1b57 100644
--- a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
+++ b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
@@ -56,35 +56,6 @@ func GetDeliveryServiceSSLKeysObj(xmlID string, version string, tx *sql.Tx, auth
 			return nil // not found
 		}
 		if err := json.Unmarshal(ro[0].Value, &key); err != nil {
-			return errors.New("unmarshalling Riak result: " + err.Error())
-		}
-		found = true
-		return nil
-	})
-	if err != nil {
-		return key, false, err
-	}
-	return key, found, nil
-}
-
-func GetDeliveryServiceSSLKeysObjTx(xmlID string, version string, tx *sql.Tx, authOpts *riak.AuthOptions) (tc.DeliveryServiceSSLKeys, bool, error) {
-	key := tc.DeliveryServiceSSLKeys{}
-	if version == "" {
-		xmlID += "-latest"
-	} else {
-		xmlID += "-" + version
-	}
-	found := false
-	err := WithClusterTx(tx, authOpts, func(cluster StorageCluster) error {
-		// get the deliveryservice ssl keys by xmlID and version
-		ro, err := FetchObjectValues(xmlID, DeliveryServiceSSLKeysBucket, cluster)
-		if err != nil {
-			return err
-		}
-		if len(ro) == 0 {
-			return nil // not found
-		}
-		if err := json.Unmarshal(ro[0].Value, &key); err != nil {
 			log.Errorf("failed at unmarshaling sslkey response: %s\n", err)
 			return errors.New("unmarshalling Riak result: " + err.Error())
 		}
@@ -118,27 +89,6 @@ func PutDeliveryServiceSSLKeysObj(key tc.DeliveryServiceSSLKeys, tx *sql.Tx, aut
 	return err
 }
 
-func PutDeliveryServiceSSLKeysObjTx(key tc.DeliveryServiceSSLKeys, tx *sql.Tx, authOpts *riak.AuthOptions) error {
-	keyJSON, err := json.Marshal(&key)
-	if err != nil {
-		return errors.New("marshalling key: " + err.Error())
-	}
-	err = WithClusterTx(tx, authOpts, func(cluster StorageCluster) error {
-		obj := &riak.Object{
-			ContentType:     "text/json",
-			Charset:         "utf-8",
-			ContentEncoding: "utf-8",
-			Key:             MakeDSSSLKeyKey(key.DeliveryService, string(key.Version)),
-			Value:           []byte(keyJSON),
-		}
-		if err = SaveObject(obj, DeliveryServiceSSLKeysBucket, cluster); err != nil {
-			return errors.New("saving Riak object: " + err.Error())
-		}
-		return nil
-	})
-	return err
-}
-
 func Ping(tx *sql.Tx, authOpts *riak.AuthOptions) (tc.RiakPingResp, error) {
 	servers, err := GetRiakServers(tx)
 	if err != nil {
@@ -235,28 +185,14 @@ func GetBucketKey(tx *sql.Tx, authOpts *riak.AuthOptions, bucket string, key str
 	return val, found, nil
 }
 
-func DeleteDSSSLKeys(tx *sql.Tx, authOpts *riak.AuthOptions, ds tc.DeliveryServiceName, version string) error {
-	if version == "" {
-		version = "latest"
-	}
-	key := string(ds) + "-" + version
-
-	cluster, err := GetRiakClusterTx(tx, authOpts)
-	if err != nil {
-		return errors.New("getting riak cluster: " + err.Error())
-	}
-	if err = cluster.Start(); err != nil {
-		return errors.New("starting riak cluster: " + err.Error())
-	}
-	defer func() {
-		if err := cluster.Stop(); err != nil {
-			log.Errorln("stopping Riak cluster: " + err.Error())
+func DeleteDSSSLKeys(tx *sql.Tx, authOpts *riak.AuthOptions, xmlID string, version string) error {
+	err := WithClusterTx(tx, authOpts, func(cluster StorageCluster) error {
+		if err := DeleteObject(MakeDSSSLKeyKey(xmlID, version), DeliveryServiceSSLKeysBucket, cluster); err != nil {
+			return errors.New("deleting SSL keys: " + err.Error())
 		}
-	}()
-	if err := DeleteObject(key, DeliveryServiceSSLKeysBucket, cluster); err != nil {
-		return errors.New("deleting SSL keys: " + err.Error())
-	}
-	return nil
+		return nil
+	})
+	return err
 }
 
 // GetURLSigConfigFileName returns the filename of the Apache Traffic Server URLSig config file
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index f9c2c84..72e7d05 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -361,13 +361,6 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{1.1, http.MethodPut, `cdns/{id}/snapshot/?$`, crconfig.SnapshotHandler, auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodPut, `snapshot/{cdn}/?$`, crconfig.SnapshotHandler, auth.PrivLevelOperations, Authenticated, nil},
 
-		//SSLKeys deliveryservice endpoints here that are marked  marked as '-wip' need to have tenancy checks added
-
-		{1.3, http.MethodGet, `deliveryservices-wip/xmlId/{xmlID}/sslkeys$`, deliveryservice.GetSSLKeysByXMLID, auth.PrivLevelAdmin, Authenticated, nil},
-		{1.3, http.MethodGet, `deliveryservices-wip/hostname/{hostName}/sslkeys$`, deliveryservice.GetSSLKeysByHostName, auth.PrivLevelAdmin, Authenticated, nil},
-		{1.3, http.MethodPost, `deliveryservices-wip/hostname/{hostName}/sslkeys/add$`, deliveryservice.AddSSLKeys, auth.PrivLevelAdmin, Authenticated, nil},
-		{1.3, http.MethodGet, `deliveryservices/xmlId/{name}/sslkeys/delete$`, deliveryservice.DeleteSSLKeys, auth.PrivLevelAdmin, Authenticated, nil},
-
 		////DeliveryServices
 		{1.3, http.MethodGet, `deliveryservices/?(\.json)?$`, api.ReadHandler(deliveryservice.GetTypeV13Factory()), auth.PrivLevelReadOnly, Authenticated, nil},
 		{1.1, http.MethodGet, `deliveryservices/?(\.json)?$`, api.ReadHandler(deliveryservice.GetTypeV12Factory()), auth.PrivLevelReadOnly, Authenticated, nil},
@@ -381,6 +374,10 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{1.1, http.MethodDelete, `deliveryservices/{id}/?(\.json)?$`, api.DeleteHandler(deliveryservice.GetTypeV12Factory()), auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodGet, `deliveryservices/{id}/servers/eligible/?(\.json)?$`, deliveryservice.GetServersEligible, auth.PrivLevelReadOnly, Authenticated, nil},
 
+		{1.1, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLID, auth.PrivLevelAdmin, Authenticated, nil},
+		{1.1, http.MethodGet, `deliveryservices/hostname/{hostname}/sslkeys$`, deliveryservice.GetSSLKeysByHostName, auth.PrivLevelAdmin, Authenticated, nil},
+		{1.1, http.MethodPost, `deliveryservices/sslkeys/add$`, deliveryservice.AddSSLKeys, auth.PrivLevelAdmin, Authenticated, nil},
+		{1.1, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys/delete$`, deliveryservice.DeleteSSLKeys, auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodPost, `deliveryservices/sslkeys/generate/?(\.json)?$`, deliveryservice.GenerateSSLKeys, auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodPost, `deliveryservices/xmlId/{name}/urlkeys/copyFromXmlId/{copy-name}/?(\.json)?$`, deliveryservice.CopyURLKeys, auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodPost, `deliveryservices/xmlId/{name}/urlkeys/generate/?(\.json)?$`, deliveryservice.GenerateURLKeys, auth.PrivLevelOperations, Authenticated, nil},


[trafficcontrol] 04/04: Fix riak content-type json header, use JSONIntStr for the version

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 11a57ad0f66be98e8c5bc606a1c0c0d88a643235
Author: Rawlin Peters <ra...@comcast.com>
AuthorDate: Tue Sep 25 15:21:58 2018 -0600

    Fix riak content-type json header, use JSONIntStr for the version
---
 lib/go-tc/deliveryservice_ssl_keys.go                 |  6 ++----
 lib/go-util/num.go                                    | 19 ++++++++-----------
 .../traffic_ops_golang/deliveryservice/keys.go        |  9 ++-------
 .../traffic_ops_golang/deliveryservice/sslkeys.go     |  7 +------
 traffic_ops/traffic_ops_golang/riaksvc/dsutil.go      |  8 ++++----
 5 files changed, 17 insertions(+), 32 deletions(-)

diff --git a/lib/go-tc/deliveryservice_ssl_keys.go b/lib/go-tc/deliveryservice_ssl_keys.go
index 007be74..e9d0ac5 100644
--- a/lib/go-tc/deliveryservice_ssl_keys.go
+++ b/lib/go-tc/deliveryservice_ssl_keys.go
@@ -45,7 +45,7 @@ type DeliveryServiceSSLKeys struct {
 	Country         string                            `json:"country,omitempty"`
 	State           string                            `json:"state,omitempty"`
 	Key             string                            `json:"key"`
-	Version         util.JSONNumAsStr                 `json:"version"`
+	Version         util.JSONIntStr                   `json:"version"`
 	Certificate     DeliveryServiceSSLKeysCertificate `json:"certificate,omitempty"`
 }
 
@@ -60,7 +60,7 @@ type DeliveryServiceSSLKeysReq struct {
 	State           *string `json:"state,omitempty"`
 	// Key is the XMLID of the delivery service
 	Key         *string                            `json:"key"`
-	Version     *util.JSONNumAsStr                 `json:"version"`
+	Version     *util.JSONIntStr                   `json:"version"`
 	Certificate *DeliveryServiceSSLKeysCertificate `json:"certificate,omitempty"`
 }
 
@@ -90,8 +90,6 @@ func (r *DeliveryServiceSSLKeysReq) validateSharedRequiredRequestFields() []stri
 	}
 	if r.Version == nil {
 		errs = append(errs, "version required")
-	} else if _, err := strconv.Atoi(string(*r.Version)); err != nil {
-		errs = append(errs, "version must parse to an integer")
 	}
 	if checkNilOrEmpty(r.Key) {
 		errs = append(errs, "key required")
diff --git a/lib/go-util/num.go b/lib/go-util/num.go
index a100170..a9dd450 100644
--- a/lib/go-util/num.go
+++ b/lib/go-util/num.go
@@ -80,6 +80,14 @@ func (i *JSONIntStr) UnmarshalJSON(d []byte) error {
 	return nil
 }
 
+func (i JSONIntStr) ToInt64() int64 {
+	return int64(i)
+}
+
+func (i JSONIntStr) String() string {
+	return strconv.FormatInt(int64(i), 10)
+}
+
 // JSONNumAsStr unmarshals JSON strings or numbers into strings
 // This is designed to handle backwards-compatibility for old Perl endpoints which accept both. Please do not use this for new endpoints or new APIs, APIs should be well-typed.
 type JSONNumAsStr string
@@ -95,17 +103,6 @@ func (s *JSONNumAsStr) UnmarshalJSON(d []byte) error {
 	return nil
 }
 
-func (s *JSONNumAsStr) ToInt() (int, error) {
-	if s == nil {
-		return 0, errors.New("cannot parse nil JSONNumAsStr to int")
-	}
-	i, err := strconv.Atoi(string(*s))
-	if err != nil {
-		return 0, errors.New("parsing JSONNumAsStr to int: " + err.Error())
-	}
-	return i, nil
-}
-
 // BytesLenSplit splits the given byte array into an n-length arrays. If n > len(s), returns a slice with a single []byte containing all of s. If n <= 0, returns an empty slice.
 func BytesLenSplit(s []byte, n int) [][]byte {
 	ss := [][]byte{}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
index 0ac0aae..0c5a1a9 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
@@ -79,12 +79,7 @@ func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("putting SSL keys in Riak for delivery service '"+*req.DeliveryService+"': "+err.Error()))
 		return
 	}
-	version, err := req.Version.ToInt()
-	if err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("adding SSL keys to delivery service '"+*req.DeliveryService+"': "+err.Error()), nil)
-		return
-	}
-	if err := updateSSLKeyVersion(*req.DeliveryService, version, inf.Tx.Tx); err != nil {
+	if err := updateSSLKeyVersion(*req.DeliveryService, req.Version.ToInt64(), inf.Tx.Tx); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("adding SSL keys to delivery service '"+*req.DeliveryService+"': "+err.Error()))
 		return
 	}
@@ -232,7 +227,7 @@ func DeleteSSLKeys(w http.ResponseWriter, r *http.Request) {
 	api.WriteResp(w, r, "Successfully deleted ssl keys for "+xmlID)
 }
 
-func updateSSLKeyVersion(xmlID string, version int, tx *sql.Tx) error {
+func updateSSLKeyVersion(xmlID string, version int64, tx *sql.Tx) error {
 	q := `UPDATE deliveryservice SET ssl_key_version = $1 WHERE xml_id = $2`
 	if _, err := tx.Exec(q, version, xmlID); err != nil {
 		return errors.New("updating delivery service ssl_key_version: " + err.Error())
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
index 5f4e602..34bd8b2 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
@@ -52,12 +52,7 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating and putting SSL keys: "+err.Error()))
 		return
 	}
-	version, err := req.Version.ToInt()
-	if err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("generating SSL keys for delivery service '"+*req.DeliveryService+"': "+err.Error()), nil)
-		return
-	}
-	if err := updateSSLKeyVersion(*req.DeliveryService, version, inf.Tx.Tx); err != nil {
+	if err := updateSSLKeyVersion(*req.DeliveryService, req.Version.ToInt64(), inf.Tx.Tx); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating SSL keys for delivery service '"+*req.DeliveryService+"': "+err.Error()))
 		return
 	}
diff --git a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
index eb4c52e..a912530 100644
--- a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
+++ b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
@@ -75,17 +75,17 @@ func PutDeliveryServiceSSLKeysObj(key tc.DeliveryServiceSSLKeys, tx *sql.Tx, aut
 	}
 	err = WithClusterTx(tx, authOpts, func(cluster StorageCluster) error {
 		obj := &riak.Object{
-			ContentType:     "text/json",
+			ContentType:     "application/json",
 			Charset:         "utf-8",
 			ContentEncoding: "utf-8",
-			Key:             MakeDSSSLKeyKey(key.DeliveryService, string(key.Version)),
+			Key:             MakeDSSSLKeyKey(key.DeliveryService, key.Version.String()),
 			Value:           []byte(keyJSON),
 		}
-		if err = SaveObject(obj, DeliveryServiceSSLKeysBucket, cluster); err != nil {
+		if err := SaveObject(obj, DeliveryServiceSSLKeysBucket, cluster); err != nil {
 			return errors.New("saving Riak object: " + err.Error())
 		}
 		obj.Key = MakeDSSSLKeyKey(key.DeliveryService, DSSSLKeyVersionLatest)
-		if err = SaveObject(obj, DeliveryServiceSSLKeysBucket, cluster); err != nil {
+		if err := SaveObject(obj, DeliveryServiceSSLKeysBucket, cluster); err != nil {
 			return errors.New("saving Riak object: " + err.Error())
 		}
 		return nil


[trafficcontrol] 03/04: Fix DS SSL API request validation, tenancy checks, versions

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 26a30b2261aa8548f63cdb2aa28fc1ddfb183478
Author: Rawlin Peters <ra...@comcast.com>
AuthorDate: Mon Sep 24 16:15:34 2018 -0600

    Fix DS SSL API request validation, tenancy checks, versions
    
    Validate API requests for cert generation and addition separately, add
    tenancy checks to cert generation, only store the numeric version of the
    SSL key in the Riak record (not "latest"), and update the
    deliveryservice.ssl_key_version to the requested version of the ssl key
    add/generate request.
---
 lib/go-tc/deliveryservice_ssl_keys.go              | 91 +++++++++++++++-------
 lib/go-util/num.go                                 | 11 +++
 .../traffic_ops_golang/deliveryservice/keys.go     | 47 +++++++----
 .../traffic_ops_golang/deliveryservice/sslkeys.go  | 36 +++++----
 traffic_ops/traffic_ops_golang/riaksvc/dsutil.go   |  4 +
 5 files changed, 133 insertions(+), 56 deletions(-)

diff --git a/lib/go-tc/deliveryservice_ssl_keys.go b/lib/go-tc/deliveryservice_ssl_keys.go
index bab764a..007be74 100644
--- a/lib/go-tc/deliveryservice_ssl_keys.go
+++ b/lib/go-tc/deliveryservice_ssl_keys.go
@@ -34,13 +34,6 @@ type DeliveryServiceSSLKeysResponse struct {
 	Response DeliveryServiceSSLKeys `json:"response"`
 }
 
-// DeliveryServiceSSLKeysCertificate ...
-type DeliveryServiceSSLKeysCertificate struct {
-	Crt string `json:"crt"`
-	Key string `json:"key"`
-	CSR string `json:"csr"`
-}
-
 // DeliveryServiceSSLKeys ...
 type DeliveryServiceSSLKeys struct {
 	CDN             string                            `json:"cdn,omitempty"`
@@ -71,6 +64,13 @@ type DeliveryServiceSSLKeysReq struct {
 	Certificate *DeliveryServiceSSLKeysCertificate `json:"certificate,omitempty"`
 }
 
+// DeliveryServiceSSLKeysCertificate ...
+type DeliveryServiceSSLKeysCertificate struct {
+	Crt string `json:"crt"`
+	Key string `json:"key"`
+	CSR string `json:"csr"`
+}
+
 func (r *DeliveryServiceSSLKeysReq) Sanitize() {
 	// DeliveryService and Key are the same value, so if the user sent one but not the other, set the missing one, in the principle of "be liberal in what you accept."
 	if r.DeliveryService == nil && r.Key != nil {
@@ -80,53 +80,92 @@ func (r *DeliveryServiceSSLKeysReq) Sanitize() {
 		k := *r.DeliveryService // sqlx fails with aliased pointers, so make a new one
 		r.Key = &k
 	}
-	if r.Version == nil {
-		numStr := util.JSONNumAsStr("")
-		r.Version = &numStr
-	}
 }
 
-func (r *DeliveryServiceSSLKeysReq) Validate(tx *sql.Tx) error {
-	r.Sanitize()
+// validateSharedRequiredRequestFields validates the request fields that are shared and required by both 'add' and 'generate' requests
+func (r *DeliveryServiceSSLKeysReq) validateSharedRequiredRequestFields() []string {
 	errs := []string{}
-	if r.CDN == nil {
+	if checkNilOrEmpty(r.CDN) {
 		errs = append(errs, "cdn required")
 	}
-	if r.Key == nil {
+	if r.Version == nil {
+		errs = append(errs, "version required")
+	} else if _, err := strconv.Atoi(string(*r.Version)); err != nil {
+		errs = append(errs, "version must parse to an integer")
+	}
+	if checkNilOrEmpty(r.Key) {
 		errs = append(errs, "key required")
 	}
-	if r.DeliveryService == nil {
+	if checkNilOrEmpty(r.DeliveryService) {
 		errs = append(errs, "deliveryservice required")
 	}
 	if r.Key != nil && r.DeliveryService != nil && *r.Key != *r.DeliveryService {
 		errs = append(errs, "deliveryservice and key must match")
 	}
-	if r.BusinessUnit == nil {
+	if checkNilOrEmpty(r.HostName) {
+		errs = append(errs, "hostname required")
+	}
+	return errs
+}
+
+type DeliveryServiceAddSSLKeysReq struct {
+	DeliveryServiceSSLKeysReq
+}
+
+func (r *DeliveryServiceAddSSLKeysReq) Validate(tx *sql.Tx) error {
+	r.Sanitize()
+	errs := r.validateSharedRequiredRequestFields()
+	if r.Certificate == nil {
+		errs = append(errs, "certificate required")
+	} else {
+		if r.Certificate.Key == "" {
+			errs = append(errs, "certificate.key required")
+		}
+		if r.Certificate.Crt == "" {
+			errs = append(errs, "certificate.crt required")
+		}
+		if r.Certificate.CSR == "" {
+			errs = append(errs, "certificate.csr required")
+		}
+	}
+	if len(errs) > 0 {
+		return errors.New("missing fields: " + strings.Join(errs, "; "))
+	}
+	return nil
+}
+
+type DeliveryServiceGenSSLKeysReq struct {
+	DeliveryServiceSSLKeysReq
+}
+
+func (r *DeliveryServiceGenSSLKeysReq) Validate(tx *sql.Tx) error {
+	r.Sanitize()
+	errs := r.validateSharedRequiredRequestFields()
+	if checkNilOrEmpty(r.BusinessUnit) {
 		errs = append(errs, "businessUnit required")
 	}
-	if r.City == nil {
+	if checkNilOrEmpty(r.City) {
 		errs = append(errs, "city required")
 	}
-	if r.Organization == nil {
+	if checkNilOrEmpty(r.Organization) {
 		errs = append(errs, "organization required")
 	}
-	if r.HostName == nil {
-		errs = append(errs, "hostname required")
-	}
-	if r.Country == nil {
+	if checkNilOrEmpty(r.Country) {
 		errs = append(errs, "country required")
 	}
-	if r.State == nil {
+	if checkNilOrEmpty(r.State) {
 		errs = append(errs, "state required")
 	}
-	// version is optional
-	// certificate is optional
 	if len(errs) > 0 {
 		return errors.New("missing fields: " + strings.Join(errs, "; "))
 	}
 	return nil
 }
 
+func checkNilOrEmpty(s *string) bool {
+	return s == nil || *s == ""
+}
+
 type RiakPingResp struct {
 	Status string `json:"status"`
 	Server string `json:"server"`
diff --git a/lib/go-util/num.go b/lib/go-util/num.go
index df9516e..a100170 100644
--- a/lib/go-util/num.go
+++ b/lib/go-util/num.go
@@ -95,6 +95,17 @@ func (s *JSONNumAsStr) UnmarshalJSON(d []byte) error {
 	return nil
 }
 
+func (s *JSONNumAsStr) ToInt() (int, error) {
+	if s == nil {
+		return 0, errors.New("cannot parse nil JSONNumAsStr to int")
+	}
+	i, err := strconv.Atoi(string(*s))
+	if err != nil {
+		return 0, errors.New("parsing JSONNumAsStr to int: " + err.Error())
+	}
+	return i, nil
+}
+
 // BytesLenSplit splits the given byte array into an n-length arrays. If n > len(s), returns a slice with a single []byte containing all of s. If n <= 0, returns an empty slice.
 func BytesLenSplit(s []byte, n int) [][]byte {
 	ss := [][]byte{}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
index 830ec07..0ac0aae 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
@@ -24,7 +24,6 @@ import (
 	"crypto/x509"
 	"database/sql"
 	"encoding/base64"
-	"encoding/json"
 	"encoding/pem"
 	"errors"
 	"net/http"
@@ -52,32 +51,44 @@ func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("adding SSL keys to Riak for delivery service: Riak is not configured"))
 		return
 	}
-	keysObj := tc.DeliveryServiceSSLKeys{}
-	if err := json.NewDecoder(r.Body).Decode(&keysObj); err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("malformed JSON"), nil)
+	req := tc.DeliveryServiceAddSSLKeysReq{}
+	if err := api.Parse(r.Body, inf.Tx.Tx, &req); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parsing request: "+err.Error()), nil)
 		return
 	}
-	if userErr, sysErr, errCode := tenant.Check(inf.User, keysObj.DeliveryService, inf.Tx.Tx); userErr != nil || sysErr != nil {
+	if userErr, sysErr, errCode := tenant.Check(inf.User, *req.DeliveryService, inf.Tx.Tx); userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
-	certChain, err := verifyCertificate(keysObj.Certificate.Crt, "")
+	certChain, err := verifyCertificate(req.Certificate.Crt, "")
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("verifying certificate: "+err.Error()), nil)
 		return
 	}
-	keysObj.Certificate.Crt = certChain
-	base64EncodeCertificate(&keysObj.Certificate)
-	if err := riaksvc.PutDeliveryServiceSSLKeysObj(keysObj, inf.Tx.Tx, inf.Config.RiakAuthOptions); err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, nil, errors.New("putting SSL keys in Riak for delivery service '"+keysObj.DeliveryService+"': "+err.Error()))
+	req.Certificate.Crt = certChain
+	base64EncodeCertificate(req.Certificate)
+	dsSSLKeys := tc.DeliveryServiceSSLKeys{
+		CDN:             *req.CDN,
+		DeliveryService: *req.DeliveryService,
+		Hostname:        *req.HostName,
+		Key:             *req.Key,
+		Version:         *req.Version,
+		Certificate:     *req.Certificate,
+	}
+	if err := riaksvc.PutDeliveryServiceSSLKeysObj(dsSSLKeys, inf.Tx.Tx, inf.Config.RiakAuthOptions); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("putting SSL keys in Riak for delivery service '"+*req.DeliveryService+"': "+err.Error()))
 		return
 	}
-	keysObj.Version = riaksvc.DSSSLKeyVersionLatest
-	if err := riaksvc.PutDeliveryServiceSSLKeysObj(keysObj, inf.Tx.Tx, inf.Config.RiakAuthOptions); err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, nil, errors.New("putting latest SSL keys in Riak for delivery service '"+keysObj.DeliveryService+"': "+err.Error()))
+	version, err := req.Version.ToInt()
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("adding SSL keys to delivery service '"+*req.DeliveryService+"': "+err.Error()), nil)
+		return
+	}
+	if err := updateSSLKeyVersion(*req.DeliveryService, version, inf.Tx.Tx); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("adding SSL keys to delivery service '"+*req.DeliveryService+"': "+err.Error()))
 		return
 	}
-	api.WriteResp(w, r, "Successfully added ssl keys for "+keysObj.DeliveryService)
+	api.WriteResp(w, r, "Successfully added ssl keys for "+*req.DeliveryService)
 }
 
 // GetSSLKeysByHostName fetches the ssl keys for a deliveryservice specified by the fully qualified hostname
@@ -221,6 +232,14 @@ func DeleteSSLKeys(w http.ResponseWriter, r *http.Request) {
 	api.WriteResp(w, r, "Successfully deleted ssl keys for "+xmlID)
 }
 
+func updateSSLKeyVersion(xmlID string, version int, tx *sql.Tx) error {
+	q := `UPDATE deliveryservice SET ssl_key_version = $1 WHERE xml_id = $2`
+	if _, err := tx.Exec(q, version, xmlID); err != nil {
+		return errors.New("updating delivery service ssl_key_version: " + err.Error())
+	}
+	return nil
+}
+
 // returns the cdn_id found by domainname.
 func getCDNIDByDomainname(domainName string, tx *sql.Tx) (int64, bool, error) {
 	cdnID := int64(0)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
index 16b87cf..5f4e602 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go
@@ -28,6 +28,7 @@ import (
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/riaksvc"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
 )
 
 func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) {
@@ -38,22 +39,34 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) {
 	}
 	defer inf.Close()
 
-	req := tc.DeliveryServiceSSLKeysReq{}
+	req := tc.DeliveryServiceGenSSLKeysReq{}
 	if err := api.Parse(r.Body, inf.Tx.Tx, &req); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("parsing request: "+err.Error()), nil)
 		return
 	}
-
+	if userErr, sysErr, errCode := tenant.Check(inf.User, *req.DeliveryService, inf.Tx.Tx); userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+		return
+	}
 	if err := generatePutRiakKeys(req, inf.Tx.Tx, inf.Config); err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating and putting SSL keys: "+err.Error()))
 		return
 	}
+	version, err := req.Version.ToInt()
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("generating SSL keys for delivery service '"+*req.DeliveryService+"': "+err.Error()), nil)
+		return
+	}
+	if err := updateSSLKeyVersion(*req.DeliveryService, version, inf.Tx.Tx); err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating SSL keys for delivery service '"+*req.DeliveryService+"': "+err.Error()))
+		return
+	}
 	api.WriteResp(w, r, "Successfully created ssl keys for "+*req.DeliveryService)
 }
 
 // generatePutRiakKeys generates a certificate, csr, and key from the given request, and insert it into the Riak key database.
 // The req MUST be validated, ensuring required fields exist.
-func generatePutRiakKeys(req tc.DeliveryServiceSSLKeysReq, tx *sql.Tx, cfg *config.Config) error {
+func generatePutRiakKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, cfg *config.Config) error {
 	dsSSLKeys := tc.DeliveryServiceSSLKeys{
 		CDN:             *req.CDN,
 		DeliveryService: *req.DeliveryService,
@@ -66,22 +79,13 @@ func generatePutRiakKeys(req tc.DeliveryServiceSSLKeysReq, tx *sql.Tx, cfg *conf
 		Key:             *req.Key,
 		Version:         *req.Version,
 	}
-	if req.Certificate != nil {
-		dsSSLKeys.Certificate = *req.Certificate
-	} else {
-		csr, crt, key, err := GenerateCert(*req.HostName, *req.Country, *req.City, *req.State, *req.Organization, *req.BusinessUnit)
-		if err != nil {
-			return errors.New("generating certificate: " + err.Error())
-		}
-		dsSSLKeys.Certificate = tc.DeliveryServiceSSLKeysCertificate{Crt: string(crt), Key: string(key), CSR: string(csr)}
+	csr, crt, key, err := GenerateCert(*req.HostName, *req.Country, *req.City, *req.State, *req.Organization, *req.BusinessUnit)
+	if err != nil {
+		return errors.New("generating certificate: " + err.Error())
 	}
+	dsSSLKeys.Certificate = tc.DeliveryServiceSSLKeysCertificate{Crt: string(crt), Key: string(key), CSR: string(csr)}
 	if err := riaksvc.PutDeliveryServiceSSLKeysObj(dsSSLKeys, tx, cfg.RiakAuthOptions); err != nil {
 		return errors.New("putting riak keys: " + err.Error())
 	}
-
-	dsSSLKeys.Version = riaksvc.DSSSLKeyVersionLatest
-	if err := riaksvc.PutDeliveryServiceSSLKeysObj(dsSSLKeys, tx, cfg.RiakAuthOptions); err != nil {
-		return errors.New("putting latest riak keys: " + err.Error())
-	}
 	return nil
 }
diff --git a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
index 1ad1b57..eb4c52e 100644
--- a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
+++ b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
@@ -84,6 +84,10 @@ func PutDeliveryServiceSSLKeysObj(key tc.DeliveryServiceSSLKeys, tx *sql.Tx, aut
 		if err = SaveObject(obj, DeliveryServiceSSLKeysBucket, cluster); err != nil {
 			return errors.New("saving Riak object: " + err.Error())
 		}
+		obj.Key = MakeDSSSLKeyKey(key.DeliveryService, DSSSLKeyVersionLatest)
+		if err = SaveObject(obj, DeliveryServiceSSLKeysBucket, cluster); err != nil {
+			return errors.New("saving Riak object: " + err.Error())
+		}
 		return nil
 	})
 	return err


[trafficcontrol] 02/04: Update CHANGELOG.md

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 62c3c21407f8a8af22c1914d955ffe0bfa88a4b5
Author: Rawlin Peters <ra...@comcast.com>
AuthorDate: Thu Sep 20 16:53:40 2018 -0600

    Update CHANGELOG.md
---
 CHANGELOG.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6829854..7ae2606 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
   - /api/1.3/origins `(GET,POST,PUT,DELETE)`
   - /api/1.3/coordinates `(GET,POST,PUT,DELETE)`
   - /api/1.3/staticdnsentries `(GET,POST,PUT,DELETE)`
+  - /api/1.1/deliveryservices/xmlId/:xmlid/sslkeys `GET`
+  - /api/1.1/deliveryservices/hostname/:hostname/sslkeys `GET`
+  - /api/1.1/deliveryservices/sslkeys/add `POST`
+  - /api/1.1/deliveryservices/xmlId/:xmlid/sslkeys/delete `GET`
 - Delivery Service Origins Refactor: The Delivery Service API now creates/updates an Origin entity on Delivery Service creates/updates, and the `org_server_fqdn` column in the `deliveryservice` table has been removed. The `org_server_fqdn` data is now computed from the Delivery Service's primary origin (note: the name of the primary origin is the `xml_id` of its delivery service).
 - Cachegroup-Coordinate Refactor: The Cachegroup API now creates/updates a Coordinate entity on Cachegroup creates/updates, and the `latitude` and `longitude` columns in the `cachegroup` table have been replaced with `coordinate` (a foreign key to Coordinate). Coordinates created from Cachegroups are given the name `from_cachegroup_\<cachegroup name\>`.
 - Geolocation-based Client Steering: two new steering target types are available to use for `CLIENT_STEERING` delivery services: `STEERING_GEO_ORDER` and `STEERING_GEO_WEIGHT`. When targets of these types have an Origin with a Coordinate, Traffic Router will order and prioritize them based upon the shortest total distance from client -> edge -> origin. Co-located targets are grouped together and can be weighted or ordered within the same location using `STEERING_GEO_WEIGHT` or `STEERING_ [...]