github.com/Venafi/vcert/v5@v5.10.2/examples/simple-cli/main.go (about)

     1  /*
     2   * Copyright 2018 Venafi, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *  http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package main
    18  
    19  import (
    20  	// nolint:gosec // TODO: figure out a way to obtain cert thumbprint to remove the use of weak cryptographic primitive (G401)
    21  	"crypto/sha1"
    22  	"crypto/x509"
    23  	"crypto/x509/pkix"
    24  	"encoding/pem"
    25  	"fmt"
    26  	"io/ioutil"
    27  	t "log"
    28  	"math/big"
    29  	"net"
    30  	"os"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/Venafi/vcert/v5"
    35  	"github.com/Venafi/vcert/v5/pkg/certificate"
    36  	"github.com/Venafi/vcert/v5/pkg/endpoint"
    37  	"github.com/Venafi/vcert/v5/pkg/util"
    38  	"github.com/Venafi/vcert/v5/pkg/venafi/tpp"
    39  )
    40  
    41  const (
    42  	name    = "example-certificate-client"
    43  	version = "v0.0.1"
    44  )
    45  
    46  func main() {
    47  
    48  	if len(os.Args) != 2 || os.Args[1] == "" {
    49  		t.Fatalf("Usage: ./$0 common.name.venafi.example.com")
    50  	}
    51  	var commonName = os.Args[1]
    52  
    53  	//
    54  	// 0. Get client instance based on connection config
    55  	//
    56  	config := tppConfig
    57  	//config := cloudConfig
    58  	//config := mockConfig
    59  	userAgent := fmt.Sprintf("%s/%s %s", name, version, util.DefaultUserAgent)
    60  	config.UserAgent = &userAgent
    61  	c, err := vcert.NewClient(config)
    62  	if err != nil {
    63  		t.Fatalf("could not connect to endpoint: %s", err)
    64  	}
    65  
    66  	//
    67  	// 1.1. Compose request object
    68  	//
    69  	//Not all Venafi Cloud providers support IPAddress and EmailAddresses extensions.
    70  	var enrollReq = &certificate.Request{}
    71  	switch {
    72  	case config.ConnectorType == endpoint.ConnectorTypeTPP || config.ConnectorType == endpoint.ConnectorTypeFake:
    73  		enrollReq = &certificate.Request{
    74  			Subject: pkix.Name{
    75  				CommonName:         commonName,
    76  				Organization:       []string{"Venafi.com"},
    77  				OrganizationalUnit: []string{"Integration Team"},
    78  				Locality:           []string{"Salt Lake"},
    79  				Province:           []string{"Salt Lake"},
    80  				Country:            []string{"US"},
    81  			},
    82  			DNSNames: []string{"www.client.venafi.example.com", "ww1.client.venafi.example.com"},
    83  
    84  			EmailAddresses: []string{"e1@venafi.example.com", "e2@venafi.example.com"},
    85  			IPAddresses:    []net.IP{net.IPv4(127, 0, 0, 1), net.IPv4(127, 0, 0, 2)},
    86  			CsrOrigin:      certificate.LocalGeneratedCSR,
    87  			KeyType:        certificate.KeyTypeRSA,
    88  			KeyLength:      2048,
    89  			ChainOption:    certificate.ChainOptionRootLast,
    90  			KeyPassword:    os.Getenv(util.ENV_DUMMY_PASS),
    91  			//Before setting custom field in request you need to configure custom field on TPP
    92  			CustomFields: []certificate.CustomField{
    93  				{Name: "custom", Value: "2019-12-10"},
    94  			},
    95  		}
    96  	case config.ConnectorType == endpoint.ConnectorTypeCloud:
    97  		enrollReq = &certificate.Request{
    98  			Subject: pkix.Name{
    99  				CommonName:         commonName,
   100  				Organization:       []string{"Venafi.com"},
   101  				OrganizationalUnit: []string{"Integration Team"},
   102  				Locality:           []string{"Salt Lake"},
   103  				Province:           []string{"Salt Lake"},
   104  				Country:            []string{"US"},
   105  			},
   106  			DNSNames:    []string{"www.client.venafi.example.com", "ww1.client.venafi.example.com"},
   107  			CsrOrigin:   certificate.LocalGeneratedCSR,
   108  			KeyType:     certificate.KeyTypeRSA,
   109  			KeyLength:   2048,
   110  			ChainOption: certificate.ChainOptionRootLast,
   111  			KeyPassword: os.Getenv(util.ENV_DUMMY_PASS),
   112  		}
   113  
   114  	}
   115  
   116  	//
   117  	// 1.2. Generate private key and certificate request (CSR) based on request's options
   118  	//
   119  	err = c.GenerateRequest(nil, enrollReq)
   120  	if err != nil {
   121  		t.Fatalf("could not generate certificate request: %s", err)
   122  	}
   123  
   124  	//
   125  	// 1.3. Submit certificate request, get request ID as a response
   126  	//
   127  	requestID, err := c.RequestCertificate(enrollReq)
   128  	if err != nil {
   129  		t.Fatalf("could not submit certificate request: %s", err)
   130  	}
   131  	t.Printf("Successfully submitted certificate request. Will pickup certificate by ID %s", requestID)
   132  
   133  	//
   134  	// 1.4. Retrieve certificate using request ID obtained on previous step, get PEM collection as a response
   135  	//
   136  	pickupReq := &certificate.Request{
   137  		PickupID: requestID,
   138  		Timeout:  180 * time.Second,
   139  	}
   140  	pcc, err := c.RetrieveCertificate(pickupReq)
   141  	if err != nil {
   142  		t.Fatalf("could not retrieve certificate using requestId %s: %s", requestID, err)
   143  	}
   144  
   145  	//
   146  	// 1.5. (optional) Add certificate's private key to PEM collection
   147  	//
   148  	_ = pcc.AddPrivateKey(enrollReq.PrivateKey, []byte(enrollReq.KeyPassword))
   149  
   150  	t.Printf("Successfully picked up certificate for %s", commonName)
   151  	pp(pcc)
   152  
   153  	//
   154  	// 2.1. Compose renewal object
   155  	//
   156  	renewReq := &certificate.RenewalRequest{
   157  		// certificate is identified using DN
   158  		CertificateDN: requestID,
   159  		// ..or SHA1 Thumbprint
   160  		// Thumbprint: "",
   161  		//CertificateRequest: certificate.Request{}
   162  	}
   163  
   164  	//
   165  	// 2.2. Submit renewal request
   166  	//
   167  	newRequestID, err := c.RenewCertificate(renewReq)
   168  	if err != nil {
   169  		t.Fatalf("could not submit certificate renewal request: %s", err)
   170  	}
   171  	t.Printf("Successfully submitted certificate renewal request. Will pickup certificate by ID %s", newRequestID)
   172  
   173  	//
   174  	// 2.3. Retrieve certificate using request ID obtained on previous step, get PEM collection as a response
   175  	//
   176  	renewRetrieveReq := &certificate.Request{
   177  		PickupID: newRequestID,
   178  		Timeout:  180 * time.Second,
   179  	}
   180  	pcc2, err := c.RetrieveCertificate(renewRetrieveReq)
   181  	if err != nil {
   182  		t.Fatalf("could not retrieve certificate using requestId %s: %s", requestID, err)
   183  	}
   184  
   185  	t.Printf("Successfully retrieved renewed certificate for %s", commonName)
   186  	t.Printf("Old serial number %s", getSerial(pcc.Certificate))
   187  	t.Printf("New serial number %s", getSerial(pcc2.Certificate))
   188  
   189  	//
   190  	// 3.1. Compose revocation object
   191  	//
   192  	revokeReq := &certificate.RevocationRequest{
   193  		CertificateDN: requestID,
   194  		Reason:        "key-compromise",
   195  		Comments:      "revocation comment below",
   196  		Disable:       false,
   197  	}
   198  
   199  	//
   200  	// 3.2. Submit revocation request (not supported in Venafi Cloud)
   201  	//
   202  	if config.ConnectorType != endpoint.ConnectorTypeCloud {
   203  		err = c.RevokeCertificate(revokeReq)
   204  		if err != nil {
   205  			t.Fatalf("could not submit certificate revocation request: %s", err)
   206  		}
   207  		t.Printf("Successfully submitted revocation request for %s", requestID)
   208  	}
   209  	//
   210  	// 4. Import certificate to another object of the same Zone
   211  	//
   212  	var importReq = &certificate.ImportRequest{}
   213  	switch {
   214  	case config.ConnectorType == endpoint.ConnectorTypeTPP || config.ConnectorType == endpoint.ConnectorTypeFake:
   215  		importObjectName := fmt.Sprintf("%s-imported", commonName)
   216  		importReq = &certificate.ImportRequest{
   217  			// if PolicyDN is empty, it is taken from cfg.Zone
   218  			ObjectName:      importObjectName,
   219  			CertificateData: pcc.Certificate,
   220  			PrivateKeyData:  pcc.PrivateKey,
   221  			Password:        os.Getenv(util.ENV_DUMMY_PASS),
   222  			Reconcile:       false,
   223  		}
   224  	case config.ConnectorType == endpoint.ConnectorTypeCloud:
   225  		importObjectName := fmt.Sprintf("%s-imported", commonName)
   226  		importReq = &certificate.ImportRequest{
   227  			// if PolicyDN is empty, it is taken from cfg.Zone
   228  			ObjectName:      importObjectName,
   229  			CertificateData: pcc.Certificate,
   230  			PrivateKeyData:  "",
   231  			Reconcile:       false,
   232  		}
   233  	}
   234  	importResp, err := c.ImportCertificate(importReq)
   235  	if err != nil {
   236  		t.Fatalf("could not import certificate: %s", err)
   237  	}
   238  	pp(importReq)
   239  	pp(importResp)
   240  	t.Printf("Successfully imported certificate to %s", importResp.CertificateDN)
   241  
   242  	//
   243  	// 5. Retrieve certificate & key from new object
   244  	//
   245  	var importedRetriveReq = &certificate.Request{}
   246  	switch {
   247  	case config.ConnectorType == endpoint.ConnectorTypeTPP || config.ConnectorType == endpoint.ConnectorTypeFake:
   248  		importedRetriveReq = &certificate.Request{
   249  			PickupID:        importResp.CertificateDN,
   250  			Timeout:         180 * time.Second,
   251  			KeyPassword:     os.Getenv(util.ENV_DUMMY_PASS),
   252  			FetchPrivateKey: true,
   253  		}
   254  	case config.ConnectorType == endpoint.ConnectorTypeCloud:
   255  		//You can retrieve imported certificate by thumbprint or certificate Id.
   256  		thumbprint := calcThumbprint(pcc.Certificate)
   257  		importedRetriveReq = &certificate.Request{
   258  			Thumbprint: thumbprint,
   259  			//CertID: importResp.CertId,
   260  			Timeout: 180 * time.Second,
   261  		}
   262  	}
   263  
   264  	pcc3, err := c.RetrieveCertificate(importedRetriveReq)
   265  	if err != nil {
   266  		t.Fatalf("could not retrieve certificate using requestId %s: %s", requestID, err)
   267  	}
   268  	t.Printf("Successfully retrieved imported certificate from %s", importResp.CertificateDN)
   269  	pp(pcc3)
   270  
   271  	//
   272  	// 6. Get refresh token and refresh access token
   273  	//
   274  	if config.ConnectorType == endpoint.ConnectorTypeTPP {
   275  		var connectionTrustBundle *x509.CertPool
   276  		trustBundleFilePath := os.Getenv("TRUST_BUNDLE_PATH")
   277  		if trustBundleFilePath != "" {
   278  			buf, err := ioutil.ReadFile(trustBundleFilePath)
   279  			if err != nil {
   280  				panic(err)
   281  			}
   282  			connectionTrustBundle = x509.NewCertPool()
   283  			if !connectionTrustBundle.AppendCertsFromPEM(buf) {
   284  				panic("Failed to parse PEM trust bundle")
   285  			}
   286  		}
   287  		tppConnector, err := tpp.NewConnector(config.BaseUrl, "", false, connectionTrustBundle)
   288  		if err != nil {
   289  			t.Fatalf("could not create TPP connector: %s", err)
   290  		}
   291  
   292  		resp, err := tppConnector.GetRefreshToken(&endpoint.Authentication{
   293  			User:     os.Getenv("TPP_USER"),
   294  			Password: os.Getenv("TPP_PASSWORD"),
   295  			Scope:    "certificate:manage,revoke;", ClientId: "websdk"})
   296  		if err != nil {
   297  			panic(err)
   298  		}
   299  		fmt.Printf("Refresh token is %s", resp.Refresh_token)
   300  
   301  		auth := &endpoint.Authentication{RefreshToken: resp.Refresh_token, ClientId: "websdk"}
   302  		err = tppConnector.Authenticate(auth)
   303  		if err != nil {
   304  			t.Fatalf("err is not nil, err: %s", err)
   305  		}
   306  
   307  	}
   308  
   309  	//
   310  	// 7. Audit certificates list in zone
   311  	//
   312  
   313  	_l := 10
   314  	certList, err := c.ListCertificates(endpoint.Filter{Limit: &_l})
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  	fmt.Println("ID             Common Name              Expire")
   319  	for _, cert := range certList {
   320  		validTo := cert.ValidTo.String()
   321  		if cert.ValidTo.Before(time.Now()) {
   322  			validTo = fmt.Sprintf("\033[1;31m%s\033[0m", validTo)
   323  		}
   324  		fmt.Printf("%v    %v     %v\n", cert.ID, cert.CN, validTo)
   325  	}
   326  }
   327  
   328  func getSerial(crt string) *big.Int {
   329  	block, _ := pem.Decode([]byte(crt))
   330  	if block == nil || block.Type != "CERTIFICATE" {
   331  		t.Fatalf("could not get PEM certificate block")
   332  	}
   333  	newCert, err := x509.ParseCertificate(block.Bytes)
   334  	if err != nil {
   335  		t.Fatalf("could not parse x509 certificate: %s", err)
   336  	}
   337  	return newCert.SerialNumber
   338  }
   339  
   340  func calcThumbprint(cert string) string {
   341  	p, _ := pem.Decode([]byte(cert))
   342  	// nolint:gosec // TODO: figure out a way to obtain cert thumbprint to remove the use of weak cryptographic primitive (G401)
   343  	h := sha1.New()
   344  	_, err := h.Write(p.Bytes)
   345  	if err != nil {
   346  		t.Fatalf("could not get SHA1 hash: %s", err)
   347  	}
   348  	buf := h.Sum(nil)
   349  	return strings.ToUpper(fmt.Sprintf("%x", buf))
   350  }