8
8
"crypto/x509/pkix"
9
9
"encoding/asn1"
10
10
"encoding/json"
11
+ "errors"
12
+ "fmt"
11
13
12
- "github.com/pkg/errors"
13
14
"go.step.sm/crypto/internal/utils"
14
15
"golang.org/x/crypto/cryptobyte"
15
16
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
@@ -53,6 +54,10 @@ type CertificateRequest struct {
53
54
URIs MultiURL `json:"uris"`
54
55
SANs []SubjectAlternativeName `json:"sans"`
55
56
Extensions []Extension `json:"extensions"`
57
+ KeyUsage KeyUsage `json:"keyUsage"`
58
+ ExtKeyUsage ExtKeyUsage `json:"extKeyUsage"`
59
+ UnknownExtKeyUsage UnknownExtKeyUsage `json:"unknownExtKeyUsage"`
60
+ BasicConstraints * BasicConstraints `json:"basicConstraints"`
56
61
SignatureAlgorithm SignatureAlgorithm `json:"signatureAlgorithm"`
57
62
ChallengePassword string `json:"-"`
58
63
PublicKey interface {} `json:"-"`
@@ -83,7 +88,7 @@ func NewCertificateRequest(signer crypto.Signer, opts ...Option) (*CertificateRe
83
88
// With templates
84
89
var cr CertificateRequest
85
90
if err := json .NewDecoder (o .CertBuffer ).Decode (& cr ); err != nil {
86
- return nil , errors . Wrap ( err , "error unmarshaling certificate" )
91
+ return nil , fmt . Errorf ( "error unmarshaling certificate: %w" , err )
87
92
}
88
93
cr .PublicKey = pub
89
94
cr .Signer = signer
@@ -100,6 +105,33 @@ func NewCertificateRequest(signer crypto.Signer, opts ...Option) (*CertificateRe
100
105
cr .Extensions = append ([]Extension {ext }, cr .Extensions ... )
101
106
}
102
107
108
+ // Add KeyUsage extension if necessary.
109
+ if cr .KeyUsage != 0 && ! cr .hasExtension (oidExtensionKeyUsage ) {
110
+ ext , err := cr .KeyUsage .Extension ()
111
+ if err != nil {
112
+ return nil , err
113
+ }
114
+ cr .Extensions = append ([]Extension {ext }, cr .Extensions ... )
115
+ }
116
+
117
+ // Add ExtKeyUsage extension if necessary.
118
+ if len (cr .ExtKeyUsage ) > 0 || len (cr .UnknownExtKeyUsage ) > 0 {
119
+ ext , err := cr .ExtKeyUsage .Extension (cr .UnknownExtKeyUsage )
120
+ if err != nil {
121
+ return nil , err
122
+ }
123
+ cr .Extensions = append ([]Extension {ext }, cr .Extensions ... )
124
+ }
125
+
126
+ // Add BasicConstraints extension if necessary.
127
+ if cr .BasicConstraints != nil {
128
+ ext , err := cr .BasicConstraints .Extension ()
129
+ if err != nil {
130
+ return nil , err
131
+ }
132
+ cr .Extensions = append ([]Extension {ext }, cr .Extensions ... )
133
+ }
134
+
103
135
return & cr , nil
104
136
}
105
137
@@ -114,6 +146,12 @@ func NewCertificateRequest(signer crypto.Signer, opts ...Option) (*CertificateRe
114
146
func NewCertificateRequestFromX509 (cr * x509.CertificateRequest ) * CertificateRequest {
115
147
// Set SubjectAltName extension as critical if Subject is empty.
116
148
fixSubjectAltName (cr )
149
+ // Extracts key usage, extended key usage, and basic constraints from the
150
+ // certificate extensions. For backward compatibility, this method does not
151
+ // return an error if an extension is improperly encoded or cannot be
152
+ // decoded. In such cases, the extension is simply ignored.
153
+ parsed , _ := parseCertificateRequestExtensions (cr .Extensions )
154
+
117
155
return & CertificateRequest {
118
156
Version : cr .Version ,
119
157
Subject : newSubject (cr .Subject ),
@@ -123,6 +161,10 @@ func NewCertificateRequestFromX509(cr *x509.CertificateRequest) *CertificateRequ
123
161
IPAddresses : cr .IPAddresses ,
124
162
URIs : cr .URIs ,
125
163
Extensions : newExtensions (cr .Extensions ),
164
+ KeyUsage : parsed .KeyUsage ,
165
+ ExtKeyUsage : parsed .ExtKeyUsage ,
166
+ UnknownExtKeyUsage : parsed .UnknownExtKeyUsage ,
167
+ BasicConstraints : parsed .BasicConstraints ,
126
168
PublicKey : cr .PublicKey ,
127
169
PublicKeyAlgorithm : cr .PublicKeyAlgorithm ,
128
170
Signature : cr .Signature ,
@@ -146,7 +188,7 @@ func (c *CertificateRequest) GetCertificateRequest() (*x509.CertificateRequest,
146
188
SignatureAlgorithm : x509 .SignatureAlgorithm (c .SignatureAlgorithm ),
147
189
}, c .Signer )
148
190
if err != nil {
149
- return nil , errors . Wrap ( err , "error creating certificate request" )
191
+ return nil , fmt . Errorf ( "error creating certificate request: %w" , err )
150
192
}
151
193
152
194
// If a challenge password is provided, encode and prepend it as a challenge
@@ -193,7 +235,7 @@ func (c *CertificateRequest) addChallengePassword(asn1Data []byte) ([]byte, erro
193
235
194
236
b , err := builder .Bytes ()
195
237
if err != nil {
196
- return nil , errors . Wrap ( err , "error marshaling challenge password" )
238
+ return nil , fmt . Errorf ( "error marshaling challenge password: %w" , err )
197
239
}
198
240
challengePasswordAttr := asn1.RawValue {
199
241
FullBytes : b ,
@@ -223,7 +265,7 @@ func (c *CertificateRequest) addChallengePassword(asn1Data []byte) ([]byte, erro
223
265
// Marshal tbsCertificateRequest
224
266
tbsCSRContents , err := asn1 .Marshal (tbsCSR )
225
267
if err != nil {
226
- return nil , errors . Wrap ( err , "error creating certificate request" )
268
+ return nil , fmt . Errorf ( "error creating certificate request: %w" , err )
227
269
}
228
270
tbsCSR .Raw = tbsCSRContents
229
271
@@ -239,7 +281,7 @@ func (c *CertificateRequest) addChallengePassword(asn1Data []byte) ([]byte, erro
239
281
}
240
282
}
241
283
if ! found {
242
- return nil , errors .Errorf ("error creating certificate request: unsupported signature algorithm %s " , sigAlgoOID )
284
+ return nil , fmt .Errorf ("error creating certificate request: unsupported signature algorithm %q " , sigAlgoOID )
243
285
}
244
286
245
287
// Sign tbsCertificateRequest
@@ -253,7 +295,7 @@ func (c *CertificateRequest) addChallengePassword(asn1Data []byte) ([]byte, erro
253
295
var signature []byte
254
296
signature , err = c .Signer .Sign (rand .Reader , signed , hashFunc )
255
297
if err != nil {
256
- return nil , errors . Wrap ( err , "error creating certificate request" )
298
+ return nil , fmt . Errorf ( "error creating certificate request: %w" , err )
257
299
}
258
300
259
301
// Build new certificate request and marshal
@@ -266,7 +308,7 @@ func (c *CertificateRequest) addChallengePassword(asn1Data []byte) ([]byte, erro
266
308
},
267
309
})
268
310
if err != nil {
269
- return nil , errors . Wrap ( err , "error creating certificate request" )
311
+ return nil , fmt . Errorf ( "error creating certificate request: %w" , err )
270
312
}
271
313
return asn1Data , nil
272
314
}
@@ -351,7 +393,7 @@ func CreateCertificateRequest(commonName string, sans []string, signer crypto.Si
351
393
URIs : uris ,
352
394
}, signer )
353
395
if err != nil {
354
- return nil , errors . Wrap ( err , "error creating certificate request" )
396
+ return nil , fmt . Errorf ( "error creating certificate request: %w" , err )
355
397
}
356
398
// This should not fail
357
399
return x509 .ParseCertificateRequest (asn1Data )
@@ -368,3 +410,91 @@ func fixSubjectAltName(cr *x509.CertificateRequest) {
368
410
}
369
411
}
370
412
}
413
+
414
+ type certificateRequestParsedExtensions struct {
415
+ KeyUsage KeyUsage
416
+ ExtKeyUsage ExtKeyUsage
417
+ UnknownExtKeyUsage UnknownExtKeyUsage
418
+ BasicConstraints * BasicConstraints
419
+ }
420
+
421
+ func parseCertificateRequestExtensions (exts []pkix.Extension ) (cr certificateRequestParsedExtensions , errs error ) {
422
+ var err error
423
+ for _ , ext := range exts {
424
+ switch {
425
+ case ext .Id .Equal (oidExtensionKeyUsage ):
426
+ if cr .KeyUsage , err = parseKeyUsageExtension (ext .Value ); err != nil {
427
+ errs = errors .Join (errs , err )
428
+ }
429
+ case ext .Id .Equal (oidExtensionExtendedKeyUsage ):
430
+ if cr .ExtKeyUsage , cr .UnknownExtKeyUsage , err = parseExtKeyUsageExtension (ext .Value ); err != nil {
431
+ errs = errors .Join (errs , err )
432
+ }
433
+ case ext .Id .Equal (oidExtensionBasicConstraints ):
434
+ if cr .BasicConstraints , err = parseBasicConstraintsExtension (ext .Value ); err != nil {
435
+ errs = errors .Join (errs , err )
436
+ }
437
+ }
438
+ }
439
+
440
+ return
441
+ }
442
+
443
+ func parseKeyUsageExtension (der cryptobyte.String ) (KeyUsage , error ) {
444
+ var usageBits asn1.BitString
445
+ if ! der .ReadASN1BitString (& usageBits ) {
446
+ return 0 , errors .New ("invalid key usage" )
447
+ }
448
+
449
+ var usage int
450
+ for i := 0 ; i < 9 ; i ++ {
451
+ if usageBits .At (i ) != 0 {
452
+ usage |= 1 << uint (i )
453
+ }
454
+ }
455
+
456
+ return KeyUsage (usage ), nil
457
+ }
458
+
459
+ func parseExtKeyUsageExtension (der cryptobyte.String ) (ExtKeyUsage , UnknownExtKeyUsage , error ) {
460
+ var extKeyUsages ExtKeyUsage
461
+ var unknownUsages UnknownExtKeyUsage
462
+ if ! der .ReadASN1 (& der , cryptobyte_asn1 .SEQUENCE ) {
463
+ return nil , nil , errors .New ("invalid extended key usages" )
464
+ }
465
+ for ! der .Empty () {
466
+ var eku asn1.ObjectIdentifier
467
+ if ! der .ReadASN1ObjectIdentifier (& eku ) {
468
+ return nil , nil , errors .New ("invalid extended key usages" )
469
+ }
470
+ if extKeyUsage , ok := extKeyUsageFromOID (eku ); ok {
471
+ extKeyUsages = append (extKeyUsages , extKeyUsage )
472
+ } else {
473
+ unknownUsages = append (unknownUsages , eku )
474
+ }
475
+ }
476
+
477
+ return extKeyUsages , unknownUsages , nil
478
+ }
479
+
480
+ func parseBasicConstraintsExtension (der cryptobyte.String ) (* BasicConstraints , error ) {
481
+ var isCA bool
482
+ if ! der .ReadASN1 (& der , cryptobyte_asn1 .SEQUENCE ) {
483
+ return nil , errors .New ("invalid basic constraints" )
484
+ }
485
+ if der .PeekASN1Tag (cryptobyte_asn1 .BOOLEAN ) {
486
+ if ! der .ReadASN1Boolean (& isCA ) {
487
+ return nil , errors .New ("invalid basic constraints" )
488
+ }
489
+ }
490
+ maxPathLen := - 1
491
+ if der .PeekASN1Tag (cryptobyte_asn1 .INTEGER ) {
492
+ if ! der .ReadASN1Integer (& maxPathLen ) {
493
+ return nil , errors .New ("invalid basic constraints" )
494
+ }
495
+ }
496
+
497
+ return & BasicConstraints {
498
+ IsCA : isCA , MaxPathLen : maxPathLen ,
499
+ }, nil
500
+ }
0 commit comments