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 }