github.com/Venafi/vcert/v5@v5.10.2/pkg/certificate/request.go (about) 1 package certificate 2 3 import ( 4 "crypto" 5 "crypto/ecdsa" 6 "crypto/ed25519" 7 "crypto/rand" 8 "crypto/rsa" 9 "crypto/x509" 10 "crypto/x509/pkix" 11 "encoding/pem" 12 "fmt" 13 "net" 14 "net/url" 15 "strings" 16 "time" 17 18 "github.com/Venafi/vcert/v5/pkg/util" 19 "github.com/Venafi/vcert/v5/pkg/verror" 20 ) 21 22 // Request contains data needed to generate a certificate request 23 // CSR is a PEM-encoded Certificate Signing Request 24 type Request struct { 25 CADN string 26 Subject pkix.Name 27 DNSNames []string 28 OmitSANs bool 29 EmailAddresses []string 30 IPAddresses []net.IP 31 URIs []*url.URL 32 UPNs []string 33 // Deprecated: Attributes is deprecated from X509.CertificateRequest. See ExtraExtensions 34 // instead. Values override any extensions that would otherwise be produced based on the 35 // other fields but are overridden by any extensions specified in Attributes. 36 Attributes []pkix.AttributeTypeAndValueSET 37 // ExtraExtensions may include SAN values and ExtKeyUsage values. If these are 38 // specified as part of ExtraExtensions, they will override the other specified values. 39 ExtraExtensions []pkix.Extension 40 SignatureAlgorithm x509.SignatureAlgorithm 41 FriendlyName string 42 KeyType KeyType 43 KeyLength int 44 KeyCurve EllipticCurve 45 csr []byte // should be a PEM-encoded CSR 46 PrivateKey crypto.Signer 47 CsrOrigin CSrOriginOption 48 PickupID string 49 //Cloud Certificate ID 50 CertID string 51 ChainOption ChainOption 52 KeyPassword string 53 FetchPrivateKey bool 54 /* Thumbprint is here because *Request is used in RetrieveCertificate(). 55 Code should be refactored so that RetrieveCertificate() uses some abstract search object, instead of *Request{PickupID} */ 56 Thumbprint string 57 // Timeout usage: 58 // TPP (a.k.a TLSPDC): we use it in order to set WorkToDoTimeout, that overrides TPP default timeout waiting time for the CA to finish 59 // if the value is more than the maximum value, TPP will automatically set the maximum value supported (as of the moment of this 60 // commit, 120 seconds). 61 // Cloud (a.k.a VaaS a.k.a TLSPC) : We use this timeout in our RetrieveCertificate function which handles a retry logic 62 // TPP SSH feature: We override the http client default timeout to perform http requests. 63 // Firefly: not usage at all 64 // 65 // Note: 66 // In VCert CLI we have hardcoded 180 seconds for retrieve certificate operation. For VaaS it will set retry logic for 67 // 180 seconds and TPP will override CA timeout as the hardcoded value 68 Timeout time.Duration 69 CustomFields []CustomField 70 Location *Location 71 ValidityDuration *time.Duration 72 ValidityPeriod string //represents the validity of the certificate expressed as an ISO 8601 duration 73 IssuerHint util.IssuerHint 74 75 // Contacts allows you to configure email addresses to send notifications 76 // about the certificate. This field is TPP-specific. 77 // 78 // Note: the user who receives the notification isn't automatically given 79 // access to that certificate. Access is configured at the policy folder 80 // level; if the user doesn't permissions on that folder, they will not be 81 // able to see the certificate's status in TPP or remediate the problem 82 // through the TPP UI. 83 // 84 // When an email is used by multiple TPP identities, the first identity 85 // found is picked arbitrarily. 86 // 87 // The scope `configuration` is required. Since Contacts works by searching 88 // the emails in the same LDAP or AD as the user attached to the token, you 89 // must check that you are using a user in that same identity provider. 90 // Contacts doesn't work with the local TPP identities. Using Contacts 91 // requires adding `mail` to the list of fields searched when performing a 92 // user search, which can be configured in the Venafi Configuration Console 93 // by RDP'ing into the TPP VM. This configuration cannot be performed 94 // directly in the TPP UI. 95 Contacts []string 96 97 // Allow user to specify whether to include 98 ExtKeyUsages ExtKeyUsageSlice 99 100 // Deprecated: use ValidityDuration instead, this field is ignored if ValidityDuration is set 101 ValidityHours int 102 } 103 104 // SetCSR sets CSR from PEM or DER format 105 func (request *Request) SetCSR(csr []byte) error { 106 pemBlock, _ := pem.Decode(csr) 107 if pemBlock != nil { 108 if strings.HasSuffix(pemBlock.Type, "CERTIFICATE REQUEST") { 109 request.csr = csr 110 return nil 111 } 112 } 113 114 //Determine CSR type and use appropriate function 115 parsedCSR, err := x509.ParseCertificateRequest(csr) 116 if err != nil { 117 return err 118 } 119 if parsedCSR != nil { 120 request.csr = pem.EncodeToMemory(GetCertificateRequestPEMBlock(csr)) 121 return nil 122 } 123 return fmt.Errorf("%w: can't determine CSR type for %s", verror.UserDataError, csr) 124 } 125 126 // GetCSR returns CSR in PEM format 127 func (request *Request) GetCSR() []byte { 128 return request.csr 129 } 130 131 // GenerateCSR creates CSR for sending to server based on data from Request fields. It rewrites CSR field if it`s already filled. 132 func (request *Request) GenerateCSR() error { 133 certificateRequest := x509.CertificateRequest{} 134 certificateRequest.Subject = request.Subject 135 136 if !request.OmitSANs { 137 addSubjectAltNames(&certificateRequest, request.DNSNames, request.EmailAddresses, request.IPAddresses, request.URIs, request.UPNs) 138 } 139 140 if request.ExtKeyUsages != nil { 141 err := addExtKeyUsage(&certificateRequest, request.ExtKeyUsages) 142 if err != nil { 143 return fmt.Errorf("%w: %s %w", verror.VcertError, "failed to add requested EKUs", err) 144 } 145 } 146 147 // If ExtraExtensions are included in request, they may override the SANs and ExtKeyUsages 148 // that were included. This is by design, so that developers using the SDK are able to 149 // craft specific and strange CSRs if required by their use-case, and they won't be clobbered 150 // by other settings. 151 if request.ExtraExtensions != nil { 152 certificateRequest.ExtraExtensions = request.ExtraExtensions 153 } 154 155 certificateRequest.Attributes = request.Attributes 156 157 csr, err := x509.CreateCertificateRequest(rand.Reader, &certificateRequest, request.PrivateKey) 158 if err != nil { 159 csr = nil 160 } 161 err = request.SetCSR(csr) 162 //request.CSR = pem.EncodeToMemory(GetCertificateRequestPEMBlock(csr)) 163 return err 164 } 165 166 // GeneratePrivateKey creates private key (if it doesn`t already exist) based on request.KeyType, request.KeyLength and request.KeyCurve fileds 167 func (request *Request) GeneratePrivateKey() error { 168 if request.PrivateKey != nil { 169 return nil 170 } 171 var err error 172 switch request.KeyType { 173 case KeyTypeECDSA: 174 request.PrivateKey, err = GenerateECDSAPrivateKey(request.KeyCurve) 175 case KeyTypeED25519: 176 request.PrivateKey, err = GenerateED25519PrivateKey() 177 case KeyTypeRSA: 178 if request.KeyLength == 0 { 179 request.KeyLength = DefaultRSAlength 180 } 181 if request.KeyLength < AllSupportedKeySizes()[0] { 182 return fmt.Errorf("key Size must be %d or greater. But it is %d", AllSupportedKeySizes()[0], request.KeyLength) 183 } 184 request.PrivateKey, err = GenerateRSAPrivateKey(request.KeyLength) 185 default: 186 return fmt.Errorf("%w: unable to generate certificate request, key type %s is not supported", verror.VcertError, request.KeyType.String()) 187 } 188 return err 189 } 190 191 // CheckCertificate validate that certificate returned by server matches data in request object. It can be used for control server. 192 func (request *Request) CheckCertificate(certPEM string) error { 193 pemBlock, _ := pem.Decode([]byte(certPEM)) 194 if pemBlock == nil { 195 return fmt.Errorf("%w: invalid pem format certificate %s", verror.CertificateCheckError, certPEM) 196 } 197 if pemBlock.Type != "CERTIFICATE" { 198 return fmt.Errorf("%w: invalid pem type %s (expect CERTIFICATE)", verror.CertificateCheckError, pemBlock.Type) 199 } 200 cert, err := x509.ParseCertificate(pemBlock.Bytes) 201 if err != nil { 202 return err 203 } 204 if request.PrivateKey != nil { 205 if request.KeyType.X509Type() != cert.PublicKeyAlgorithm { 206 return fmt.Errorf("%w: unmatched key type: %s, %s", verror.CertificateCheckError, request.KeyType.X509Type(), cert.PublicKeyAlgorithm) 207 } 208 switch cert.PublicKeyAlgorithm { 209 case x509.RSA: 210 certPubKey := cert.PublicKey.(*rsa.PublicKey) 211 reqPubkey, ok := request.PrivateKey.Public().(*rsa.PublicKey) 212 if !ok { 213 return fmt.Errorf("%w: request KeyType not matched with real PrivateKey type", verror.CertificateCheckError) 214 } 215 216 if certPubKey.N.Cmp(reqPubkey.N) != 0 { 217 return fmt.Errorf("%w: unmatched key modulus", verror.CertificateCheckError) 218 } 219 case x509.ECDSA: 220 certPubkey := cert.PublicKey.(*ecdsa.PublicKey) 221 reqPubkey, ok := request.PrivateKey.Public().(*ecdsa.PublicKey) 222 if !ok { 223 return fmt.Errorf("%w: request KeyType not matched with real PrivateKey type", verror.CertificateCheckError) 224 } 225 if certPubkey.X.Cmp(reqPubkey.X) != 0 { 226 return fmt.Errorf("%w: unmatched X for elliptic keys", verror.CertificateCheckError) 227 } 228 case x509.Ed25519: 229 certPubkey := cert.PublicKey.(ed25519.PublicKey) 230 reqPubkey, ok := request.PrivateKey.Public().(ed25519.PublicKey) 231 if !ok { 232 return fmt.Errorf("%w: request KeyType not matched with real PrivateKey type", verror.CertificateCheckError) 233 } 234 if !certPubkey.Equal(reqPubkey) { 235 return fmt.Errorf("%w: unmatched elliptic ed25519 keys", verror.CertificateCheckError) 236 } 237 default: 238 return fmt.Errorf("%w: unknown key algorythm %d", verror.CertificateCheckError, cert.PublicKeyAlgorithm) 239 } 240 } else if len(request.csr) != 0 { 241 pemBlock, _ := pem.Decode(request.csr) 242 if pemBlock == nil { 243 return fmt.Errorf("%w: bad CSR: %s", verror.CertificateCheckError, string(request.csr)) 244 } 245 csr, err := x509.ParseCertificateRequest(pemBlock.Bytes) 246 if err != nil { 247 return err 248 } 249 if cert.PublicKeyAlgorithm != csr.PublicKeyAlgorithm { 250 return fmt.Errorf("%w: unmatched key type: %s, %s", verror.CertificateCheckError, cert.PublicKeyAlgorithm, csr.PublicKeyAlgorithm) 251 } 252 switch csr.PublicKeyAlgorithm { 253 case x509.RSA: 254 certPubKey := cert.PublicKey.(*rsa.PublicKey) 255 reqPubKey := csr.PublicKey.(*rsa.PublicKey) 256 if certPubKey.N.Cmp(reqPubKey.N) != 0 { 257 return fmt.Errorf("%w: unmatched key modulus", verror.CertificateCheckError) 258 } 259 case x509.ECDSA: 260 certPubKey := cert.PublicKey.(*ecdsa.PublicKey) 261 reqPubKey := csr.PublicKey.(*ecdsa.PublicKey) 262 if certPubKey.X.Cmp(reqPubKey.X) != 0 { 263 return fmt.Errorf("%w: unmatched X for elliptic keys", verror.CertificateCheckError) 264 } 265 } 266 } 267 return nil 268 }