-
Notifications
You must be signed in to change notification settings - Fork 64
/
request.go
209 lines (197 loc) · 7.18 KB
/
request.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package certificate
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"net"
"net/url"
"strings"
"time"
"github.com/Venafi/vcert/v5/pkg/util"
"github.com/Venafi/vcert/v5/pkg/verror"
)
// Request contains data needed to generate a certificate request
// CSR is a PEM-encoded Certificate Signing Request
type Request struct {
CADN string
Subject pkix.Name
DNSNames []string
OmitSANs bool
EmailAddresses []string
IPAddresses []net.IP
URIs []*url.URL
UPNs []string
Attributes []pkix.AttributeTypeAndValueSET
SignatureAlgorithm x509.SignatureAlgorithm
FriendlyName string
KeyType KeyType
KeyLength int
KeyCurve EllipticCurve
csr []byte // should be a PEM-encoded CSR
PrivateKey crypto.Signer
CsrOrigin CSrOriginOption
PickupID string
//Cloud Certificate ID
CertID string
ChainOption ChainOption
KeyPassword string
FetchPrivateKey bool
/* Thumbprint is here because *Request is used in RetrieveCertificate().
Code should be refactored so that RetrieveCertificate() uses some abstract search object, instead of *Request{PickupID} */
Thumbprint string
Timeout time.Duration
CustomFields []CustomField
Location *Location
ValidityDuration *time.Duration
ValidityPeriod string //represents the validity of the certificate expressed as an ISO 8601 duration
IssuerHint util.IssuerHint
// Deprecated: use ValidityDuration instead, this field is ignored if ValidityDuration is set
ValidityHours int
}
// SetCSR sets CSR from PEM or DER format
func (request *Request) SetCSR(csr []byte) error {
pemBlock, _ := pem.Decode(csr)
if pemBlock != nil {
if strings.HasSuffix(pemBlock.Type, "CERTIFICATE REQUEST") {
request.csr = csr
return nil
}
}
//Determine CSR type and use appropriate function
parsedCSR, err := x509.ParseCertificateRequest(csr)
if err != nil {
return err
}
if parsedCSR != nil {
request.csr = pem.EncodeToMemory(GetCertificateRequestPEMBlock(csr))
return nil
}
return fmt.Errorf("%w: can't determine CSR type for %s", verror.UserDataError, csr)
}
// GetCSR returns CSR in PEM format
func (request *Request) GetCSR() []byte {
return request.csr
}
// GenerateCSR creates CSR for sending to server based on data from Request fields. It rewrites CSR field if it`s already filled.
func (request *Request) GenerateCSR() error {
certificateRequest := x509.CertificateRequest{}
certificateRequest.Subject = request.Subject
if !request.OmitSANs {
addSubjectAltNames(&certificateRequest, request.DNSNames, request.EmailAddresses, request.IPAddresses, request.URIs, request.UPNs)
}
certificateRequest.Attributes = request.Attributes
csr, err := x509.CreateCertificateRequest(rand.Reader, &certificateRequest, request.PrivateKey)
if err != nil {
csr = nil
}
err = request.SetCSR(csr)
//request.CSR = pem.EncodeToMemory(GetCertificateRequestPEMBlock(csr))
return err
}
// GeneratePrivateKey creates private key (if it doesn`t already exist) based on request.KeyType, request.KeyLength and request.KeyCurve fileds
func (request *Request) GeneratePrivateKey() error {
if request.PrivateKey != nil {
return nil
}
var err error
switch request.KeyType {
case KeyTypeECDSA:
request.PrivateKey, err = GenerateECDSAPrivateKey(request.KeyCurve)
case KeyTypeED25519:
request.PrivateKey, err = GenerateED25519PrivateKey()
case KeyTypeRSA:
if request.KeyLength == 0 {
request.KeyLength = defaultRSAlength
}
if request.KeyLength < AllSupportedKeySizes()[0] {
return fmt.Errorf("key Size must be %d or greater. But it is %d", AllSupportedKeySizes()[0], request.KeyLength)
}
request.PrivateKey, err = GenerateRSAPrivateKey(request.KeyLength)
default:
return fmt.Errorf("%w: unable to generate certificate request, key type %s is not supported", verror.VcertError, request.KeyType.String())
}
return err
}
// CheckCertificate validate that certificate returned by server matches data in request object. It can be used for control server.
func (request *Request) CheckCertificate(certPEM string) error {
pemBlock, _ := pem.Decode([]byte(certPEM))
if pemBlock == nil {
return fmt.Errorf("%w: invalid pem format certificate %s", verror.CertificateCheckError, certPEM)
}
if pemBlock.Type != "CERTIFICATE" {
return fmt.Errorf("%w: invalid pem type %s (expect CERTIFICATE)", verror.CertificateCheckError, pemBlock.Type)
}
cert, err := x509.ParseCertificate(pemBlock.Bytes)
if err != nil {
return err
}
if request.PrivateKey != nil {
if request.KeyType.X509Type() != cert.PublicKeyAlgorithm {
return fmt.Errorf("%w: unmatched key type: %s, %s", verror.CertificateCheckError, request.KeyType.X509Type(), cert.PublicKeyAlgorithm)
}
switch cert.PublicKeyAlgorithm {
case x509.RSA:
certPubKey := cert.PublicKey.(*rsa.PublicKey)
reqPubkey, ok := request.PrivateKey.Public().(*rsa.PublicKey)
if !ok {
return fmt.Errorf("%w: request KeyType not matched with real PrivateKey type", verror.CertificateCheckError)
}
if certPubKey.N.Cmp(reqPubkey.N) != 0 {
return fmt.Errorf("%w: unmatched key modulus", verror.CertificateCheckError)
}
case x509.ECDSA:
certPubkey := cert.PublicKey.(*ecdsa.PublicKey)
reqPubkey, ok := request.PrivateKey.Public().(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("%w: request KeyType not matched with real PrivateKey type", verror.CertificateCheckError)
}
if certPubkey.X.Cmp(reqPubkey.X) != 0 {
return fmt.Errorf("%w: unmatched X for elliptic keys", verror.CertificateCheckError)
}
case x509.Ed25519:
certPubkey := cert.PublicKey.(ed25519.PublicKey)
reqPubkey, ok := request.PrivateKey.Public().(ed25519.PublicKey)
if !ok {
return fmt.Errorf("%w: request KeyType not matched with real PrivateKey type", verror.CertificateCheckError)
}
if !certPubkey.Equal(reqPubkey) {
return fmt.Errorf("%w: unmatched elliptic ed25519 keys", verror.CertificateCheckError)
}
default:
return fmt.Errorf("%w: unknown key algorythm %d", verror.CertificateCheckError, cert.PublicKeyAlgorithm)
}
} else if len(request.csr) != 0 {
pemBlock, _ := pem.Decode(request.csr)
if pemBlock == nil {
return fmt.Errorf("%w: bad CSR: %s", verror.CertificateCheckError, string(request.csr))
}
csr, err := x509.ParseCertificateRequest(pemBlock.Bytes)
if err != nil {
return err
}
if cert.PublicKeyAlgorithm != csr.PublicKeyAlgorithm {
return fmt.Errorf("%w: unmatched key type: %s, %s", verror.CertificateCheckError, cert.PublicKeyAlgorithm, csr.PublicKeyAlgorithm)
}
switch csr.PublicKeyAlgorithm {
case x509.RSA:
certPubKey := cert.PublicKey.(*rsa.PublicKey)
reqPubKey := csr.PublicKey.(*rsa.PublicKey)
if certPubKey.N.Cmp(reqPubKey.N) != 0 {
return fmt.Errorf("%w: unmatched key modulus", verror.CertificateCheckError)
}
case x509.ECDSA:
certPubKey := cert.PublicKey.(*ecdsa.PublicKey)
reqPubKey := csr.PublicKey.(*ecdsa.PublicKey)
if certPubKey.X.Cmp(reqPubKey.X) != 0 {
return fmt.Errorf("%w: unmatched X for elliptic keys", verror.CertificateCheckError)
}
}
}
return nil
}