github.com/Venafi/vcert/v5@v5.10.2/pkg/certificate/certificate.go (about) 1 /* 2 * Copyright 2018-2023 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 certificate 18 19 import ( 20 "crypto" 21 "crypto/ecdsa" 22 "crypto/ed25519" 23 "crypto/elliptic" 24 "crypto/rand" 25 "crypto/rsa" 26 "crypto/x509" 27 "encoding/pem" 28 "fmt" 29 "reflect" 30 "sort" 31 "time" 32 33 "github.com/youmark/pkcs8" 34 35 "github.com/Venafi/vcert/v5/pkg/util" 36 "github.com/Venafi/vcert/v5/pkg/verror" 37 ) 38 39 const ( 40 DefaultRSAlength int = 2048 41 ) 42 43 func AllSupportedKeySizes() []int { 44 return []int{1024, DefaultRSAlength, 3072, 4096, 8192} 45 } 46 47 //SSH Certificate structures 48 49 // SshCertRequest This request is a standard one, it will hold data for tpp request 50 // and in the future it will hold VaS data. 51 type SshCertRequest struct { 52 Template string 53 PolicyDN string 54 ObjectName string 55 DestinationAddresses []string 56 KeyId string 57 Principals []string 58 ValidityPeriod string 59 PublicKeyData string 60 Extensions []string 61 ForceCommand string 62 SourceAddresses []string 63 64 PickupID string 65 Guid string 66 IncludePrivateKeyData bool 67 PrivateKeyPassphrase string 68 PrivateKeyFormat string 69 IncludeCertificateDetails bool 70 71 Timeout time.Duration 72 } 73 74 type TPPSshCertRequest struct { 75 CADN string `json:"CADN,omitempty"` 76 PolicyDN string `json:"PolicyDN,omitempty"` 77 ObjectName string `json:"ObjectName,omitempty"` 78 DestinationAddresses []string `json:"DestinationAddresses,omitempty"` 79 KeyId string `json:"KeyId,omitempty"` 80 Principals []string `json:"Principals,omitempty"` 81 ValidityPeriod string `json:"ValidityPeriod,omitempty"` 82 PublicKeyData string `json:"PublicKeyData,omitempty"` 83 Extensions map[string]interface{} `json:"Extensions,omitempty"` 84 ForceCommand string `json:"ForceCommand,omitempty"` 85 SourceAddresses []string `json:"SourceAddresses,omitempty"` 86 IncludePrivateKeyData bool `json:"IncludePrivateKeyData,omitempty"` 87 PrivateKeyPassphrase string `json:"PrivateKeyPassphrase,omitempty"` 88 IncludeCertificateDetails bool `json:"IncludeCertificateDetails,omitempty"` 89 ProcessingTimeout string `json:"ProcessingTimeout,omitempty"` 90 } 91 92 type TppSshCertResponseInfo struct { 93 ErrorCode int 94 ErrorMessage string 95 Success bool 96 } 97 98 type TppSshCertRetrieveRequest struct { 99 Guid string 100 DN string 101 IncludePrivateKeyData bool 102 PrivateKeyPassphrase string 103 PrivateKeyFormat string 104 IncludeCertificateDetails bool 105 } 106 107 type TppSshCertOperationResponse struct { 108 ProcessingDetails ProcessingDetails 109 Guid string 110 DN string 111 CertificateData string 112 PrivateKeyData string 113 PublicKeyData string 114 CAGuid string 115 CADN string 116 CertificateDetails SshCertificateDetails 117 Response TppSshCertResponseInfo 118 } 119 120 type SshCertificateObject struct { 121 Guid string 122 DN string 123 CAGuid string 124 CADN string 125 CertificateData string 126 PrivateKeyData string 127 PublicKeyData string 128 CertificateDetails SshCertificateDetails 129 ProcessingDetails ProcessingDetails 130 } 131 132 type SshCertificateDetails struct { 133 KeyType string `json:"KeyType,omitempty"` 134 CertificateType string `json:"CertificateType,omitempty"` 135 CertificateFingerprintSHA256 string `json:"CertificateFingerprintSHA256,omitempty"` 136 CAFingerprintSHA256 string `json:"CAFingerprintSHA256,omitempty"` 137 KeyID string `json:"KeyID,omitempty"` 138 SerialNumber string `json:"SerialNumber,omitempty"` 139 Principals []string `json:"Principals,omitempty"` 140 ValidFrom int64 `json:"ValidFrom,omitempty"` 141 ValidTo int64 `json:"ValidTo,omitempty"` 142 ForceCommand string `json:"ForceCommand,omitempty"` 143 SourceAddresses []string `json:"SourceAddresses,omitempty"` 144 PublicKeyFingerprintSHA256 string `json:"PublicKeyFingerprintSHA256,omitempty"` 145 Extensions map[string]interface{} `json:"Extensions,omitempty"` 146 } 147 148 type ProcessingDetails struct { 149 Status string `json:"Status,omitempty"` 150 StatusDescription string `json:"StatusDescription,omitempty"` 151 } 152 153 type RevocationRequest struct { 154 CertificateDN string 155 Thumbprint string 156 Reason string 157 Comments string 158 Disable bool 159 } 160 161 type RetireRequest struct { 162 CertificateDN string 163 Thumbprint string 164 Description string 165 } 166 167 type RenewalRequest struct { 168 CertificateDN string // these fields are for certificate lookup on remote 169 Thumbprint string 170 CertificateRequest *Request // here CSR should be filled 171 } 172 173 type ImportRequest struct { 174 PolicyDN string 175 ObjectName string 176 CertificateData string 177 PrivateKeyData string 178 Password string 179 Reconcile bool 180 CustomFields []CustomField 181 } 182 183 type ImportResponse struct { 184 CertificateDN string `json:",omitempty"` 185 CertId string `json:",omitempty"` 186 CertificateVaultId int `json:",omitempty"` 187 Guid string `json:",omitempty"` 188 PrivateKeyVaultId int `json:",omitempty"` 189 } 190 191 type Sans struct { 192 DNS []string 193 Email []string `json:",omitempty"` 194 IP []string `json:",omitempty"` 195 URI []string `json:",omitempty"` 196 UPN []string `json:",omitempty"` 197 } 198 199 type CertificateInfo struct { 200 ID string `json:",omitempty"` 201 CN string 202 SANS Sans 203 Serial string 204 Thumbprint string 205 ValidFrom time.Time 206 ValidTo time.Time 207 } 208 209 type SearchRequest []string 210 211 type CertSearchResponse struct { 212 Certificates []CertSeachInfo `json:"Certificates"` 213 Count int `json:"TotalCount"` 214 } 215 216 type CertificateMetaData struct { 217 Approver []string `json:"Approver"` 218 CreatedOn string `json:"CreatedOn"` 219 CertificateAuthorityDN string `json:"CertificateAuthorityDN"` 220 Contact []string `json:"Contact"` 221 CreatedBy []string `json:"CreatedBy"` 222 CertificateDetails struct { 223 AIACAIssuerURL []string `json:"AIACAIssuerURL"` 224 AIAKeyIdentifier string `json:"AIAKeyIdentifier"` 225 C string `json:"C"` 226 CDPURI string `json:"CDPURI"` 227 CN string `json:"CN"` 228 EnhancedKeyUsage string `json:"EnhancedKeyUsage"` 229 Issuer string `json:"Issuer"` 230 KeyAlgorithm string `json:"KeyAlgorithm"` 231 KeySize int `json:"KeySize"` 232 KeyUsage string `json:"KeyUsage"` 233 L string `json:"L"` 234 O string `json:"O"` 235 OU []string `json:"OU"` 236 PublicKeyHash string `json:"PublicKeyHash"` 237 S string `json:"S"` 238 SKIKeyIdentifier string `json:"SKIKeyIdentifier"` 239 Serial string `json:"Serial"` 240 SignatureAlgorithm string `json:"SignatureAlgorithm"` 241 SignatureAlgorithmOID string `json:"SignatureAlgorithmOID"` 242 StoreAdded time.Time `json:"StoreAdded"` 243 Subject string `json:"Subject"` 244 TemplateMajorVersion string `json:"TemplateMajorVersion"` 245 TemplateMinorVersion string `json:"TemplateMinorVersion"` 246 TemplateName string `json:"TemplateName"` 247 TemplateOID string `json:"TemplateOID"` 248 Thumbprint string `json:"Thumbprint"` 249 ValidFrom time.Time `json:"ValidFrom"` 250 ValidTo time.Time `json:"ValidTo"` 251 } `json:"CertificateDetails"` 252 253 RenewalDetails struct { 254 City string `json:"City"` 255 Country string `json:"Country"` 256 KeySize int `json:"KeySize"` 257 Organization string `json:"Organization"` 258 OrganizationalUnit []string `json:"OrganizationalUnit"` 259 State string `json:"State"` 260 Subject string `json:"Subject"` 261 } `json:"RenewalDetails"` 262 263 ValidationDetails struct { 264 LastValidationStateUpdate time.Time `json:"LastValidationStateUpdate"` 265 NetworkValidationDisabled bool `json:"NetworkValidationDisabled"` 266 ValidationDisabled bool `json:"ValidationDisabled"` 267 } `json:"ValidationDetails"` 268 269 CustomFields []CustomFieldDetails `json:"CustomFields"` 270 271 DN string `json:"DN"` 272 Guid string `json:"Guid"` 273 ManagementType string `json:"ManagementType"` 274 Name string `json:"Name"` 275 Origin string `json:"Origin"` 276 ParentDn string `json:"ParentDn"` 277 SchemaClass string `json:"SchemaClass"` 278 } 279 type CustomFieldDetails struct { 280 Name string `json:"Name"` 281 Type string `json:"Type"` 282 Value []string `json:"Value"` 283 } 284 285 type CertSeachInfo struct { 286 CertificateRequestId string `json:"DN"` 287 CertificateRequestGuid string `json:"Guid"` 288 } 289 290 // Deprecated: GenerateRequest is deprecated 291 // Please use method Request.GenerateCSR() 292 // GenerateRequest generates a certificate request 293 // TODO: Remove usage from all libraries, deprecated 294 func GenerateRequest(request *Request, privateKey crypto.Signer) error { 295 pk := request.PrivateKey 296 request.PrivateKey = privateKey 297 err := request.GenerateCSR() 298 request.PrivateKey = pk 299 return err 300 } 301 302 func publicKey(priv crypto.Signer) crypto.PublicKey { 303 if priv != nil { 304 return priv.Public() 305 } 306 return nil 307 } 308 309 func PublicKey(priv crypto.Signer) crypto.PublicKey { 310 return publicKey(priv) 311 } 312 313 // GetPrivateKeyPEMBock gets the private key as a PEM data block 314 func GetPrivateKeyPEMBock(key crypto.Signer, format ...string) (*pem.Block, error) { 315 currentFormat := "" 316 if len(format) > 0 && format[0] != "" { 317 currentFormat = format[0] 318 } 319 switch k := key.(type) { 320 case *rsa.PrivateKey: 321 if currentFormat == "legacy-pem" { 322 return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil 323 } else { 324 dataBytes, err := pkcs8.MarshalPrivateKey(key.(*rsa.PrivateKey), nil, nil) 325 if err != nil { 326 return nil, err 327 } 328 return &pem.Block{Type: "PRIVATE KEY", Bytes: dataBytes}, err 329 } 330 case *ecdsa.PrivateKey: 331 if currentFormat == "legacy-pem" { 332 b, err := x509.MarshalECPrivateKey(k) 333 if err != nil { 334 return nil, err 335 } 336 return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil 337 } else { 338 dataBytes, err := pkcs8.MarshalPrivateKey(key.(*ecdsa.PrivateKey), nil, nil) 339 if err != nil { 340 return nil, err 341 } 342 return &pem.Block{Type: "PRIVATE KEY", Bytes: dataBytes}, err 343 } 344 case ed25519.PrivateKey: 345 if currentFormat == "legacy-pem" { 346 return nil, fmt.Errorf("%w: unable to format Key. Legacy format for ed25519 is not supported", verror.VcertError) 347 } else { 348 dataBytes, err := pkcs8.MarshalPrivateKey(key.(ed25519.PrivateKey), nil, nil) 349 if err != nil { 350 return nil, err 351 } 352 return &pem.Block{Type: "PRIVATE KEY", Bytes: dataBytes}, err 353 } 354 default: 355 return nil, fmt.Errorf("%w: unable to format Key", verror.VcertError) 356 } 357 } 358 359 // GetEncryptedPrivateKeyPEMBock gets the private key as an encrypted PEM data block 360 func GetEncryptedPrivateKeyPEMBock(key crypto.Signer, password []byte, format ...string) (*pem.Block, error) { 361 currentFormat := "" 362 if len(format) > 0 && format[0] != "" { 363 currentFormat = format[0] 364 } 365 switch k := key.(type) { 366 case *rsa.PrivateKey: 367 if currentFormat == "legacy-pem" { 368 return util.X509EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(k), password, util.PEMCipherAES256) 369 } else { 370 dataBytes, err := pkcs8.MarshalPrivateKey(key.(*rsa.PrivateKey), password, nil) 371 if err != nil { 372 return nil, err 373 } 374 return &pem.Block{Type: "ENCRYPTED PRIVATE KEY", Bytes: dataBytes}, err 375 } 376 case *ecdsa.PrivateKey: 377 if currentFormat == "legacy-pem" { 378 b, err := x509.MarshalECPrivateKey(k) 379 if err != nil { 380 return nil, err 381 } 382 return util.X509EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", b, password, util.PEMCipherAES256) 383 } else { 384 dataBytes, err := pkcs8.MarshalPrivateKey(key.(*ecdsa.PrivateKey), password, nil) 385 if err != nil { 386 return nil, err 387 } 388 return &pem.Block{Type: "ENCRYPTED PRIVATE KEY", Bytes: dataBytes}, err 389 } 390 case ed25519.PrivateKey: 391 if currentFormat == "legacy-pem" { 392 return nil, fmt.Errorf("%w: unable to format Key. Legacy format for ed25519 is not supported", verror.VcertError) 393 } else { 394 dataBytes, err := pkcs8.MarshalPrivateKey(key.(ed25519.PrivateKey), password, nil) 395 if err != nil { 396 return nil, err 397 } 398 return &pem.Block{Type: "ENCRYPTED PRIVATE KEY", Bytes: dataBytes}, err 399 } 400 default: 401 return nil, fmt.Errorf("%w: unable to format Key", verror.VcertError) 402 } 403 } 404 405 // GetCertificatePEMBlock gets the certificate as a PEM data block 406 func GetCertificatePEMBlock(cert []byte) *pem.Block { 407 return &pem.Block{Type: "CERTIFICATE", Bytes: cert} 408 } 409 410 // GetCertificateRequestPEMBlock gets the certificate request as a PEM data block 411 func GetCertificateRequestPEMBlock(request []byte) *pem.Block { 412 return &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: request} 413 } 414 415 // GenerateECDSAPrivateKey generates a new ecdsa private key using the curve specified 416 func GenerateECDSAPrivateKey(curve EllipticCurve) (crypto.Signer, error) { 417 var priv crypto.Signer 418 var c elliptic.Curve 419 var err error 420 if curve == EllipticCurveNotSet { 421 curve = EllipticCurveDefault 422 } 423 424 switch curve { 425 case EllipticCurveP521: 426 c = elliptic.P521() 427 case EllipticCurveP384: 428 c = elliptic.P384() 429 case EllipticCurveP256: 430 c = elliptic.P256() 431 case EllipticCurveED25519: 432 return nil, fmt.Errorf("%w: unable to generate ECDSA key. ED25519 curve is not supported, use GenerateED25519PrivateKey instead", verror.VcertError) 433 } 434 435 priv, err = ecdsa.GenerateKey(c, rand.Reader) 436 if err != nil { 437 return nil, err 438 } 439 440 return priv, nil 441 } 442 443 func GenerateED25519PrivateKey() (crypto.Signer, error) { 444 _, priv, err := ed25519.GenerateKey(rand.Reader) 445 if err != nil { 446 return nil, err 447 } 448 return priv, nil 449 } 450 451 // GenerateRSAPrivateKey generates a new rsa private key using the size specified 452 func GenerateRSAPrivateKey(size int) (*rsa.PrivateKey, error) { 453 priv, err := rsa.GenerateKey(rand.Reader, size) 454 if err != nil { 455 return nil, err 456 } 457 458 return priv, nil 459 } 460 461 // NewRequest duplicates new Request object based on issued certificate 462 func NewRequest(cert *x509.Certificate) *Request { 463 req := &Request{} 464 465 // First populate with *cert content 466 req.Subject = cert.Subject 467 req.DNSNames = cert.DNSNames 468 req.EmailAddresses = cert.EmailAddresses 469 req.IPAddresses = cert.IPAddresses 470 req.URIs = cert.URIs 471 req.UPNs, _ = getUserPrincipalNameSANs(cert) 472 473 req.SignatureAlgorithm = cert.SignatureAlgorithm 474 switch pub := cert.PublicKey.(type) { 475 case *rsa.PublicKey: 476 req.KeyType = KeyTypeRSA 477 req.KeyLength = pub.N.BitLen() 478 case *ecdsa.PublicKey: 479 req.KeyType = KeyTypeECDSA 480 _ = req.KeyCurve.Set(pub.Curve.Params().Name) 481 case ed25519.PublicKey: 482 req.KeyType = KeyTypeED25519 483 _ = req.KeyCurve.Set("ed25519") 484 default: 485 // vcert only works with RSA, ECDSA & Ed25519 keys 486 } 487 return req 488 } 489 490 // FindNewestCertificateWithSans finds a certificate from a list of certificates whose Sans.DNS matches and is 491 // the newest 492 func FindNewestCertificateWithSans(certificates []*CertificateInfo, sans_ *Sans) (*CertificateInfo, error) { 493 sans := Sans{} 494 495 if sans_ != nil { 496 sans.DNS = sans_.DNS 497 } 498 499 // order provided SANS-DNS 500 sort.Strings(sans.DNS) 501 502 // create local variable to hold the newest certificate 503 var newestCertificate *CertificateInfo 504 for _, certificate := range certificates { 505 // order certificate SANS before comparison 506 if certificate.SANS.DNS != nil { 507 sort.Strings(certificate.SANS.DNS) 508 } 509 // exact match SANs 510 if reflect.DeepEqual(sans.DNS, certificate.SANS.DNS) { 511 // update the certificate to the newest match 512 if newestCertificate == nil || certificate.ValidTo.Unix() > newestCertificate.ValidTo.Unix() { 513 newestCertificate = certificate 514 } 515 } 516 } 517 518 // a valid certificate has been found, return it 519 if newestCertificate != nil { 520 return newestCertificate, nil 521 } 522 523 // fail, since no valid certificate was found at this point 524 return nil, verror.NoCertificateFoundError 525 }